首页 文章 精选 留言 我的

精选列表

搜索[performance],共2021篇文章
优秀的个人博客,低调大师

Android开发者指南(17) —— Designing For Performance

性能优化 Android应用程序运行的移动设备受限于其运算能力,存储空间,及电池续航。由此,它必须是高效的。电池续航可能是一个促使你优化程序的原因,即使他看起来已经运行的足够快了。由于续航对用户的重要性,当电量耗损陡增时,意味这用户迟早会发现是由于你的程序。 虽然这份文档主要包含着细微的优化,但这些绝不能成为你软件成败的关键。选择合适的算法和数据结构永远是你最先应该考虑的事情,但这超出这份文档之外。 简介 写出高效的代码有两条基本的原则: *不作没有必要的工作。 *尽量避免内存分配。 明智的优化 这份文档是关于Android规范的细微优化,所以先确保你已经了解哪些代码需要优化,并且知道如何去衡量你所做修改所带来的效果(好或坏)。开发投入的时间是有限的,所以明智的时间规划很重要。 (更多分析和笔记参见总结。) 这份文档同时确保你在算法和数据结构上作出最佳选择的同时,考虑API选择所带来的潜在影响。使用合适的数据结构和算法比这里的任何建议都更有价值,优先考虑API版本带来的影响有助于你找到更好的实现。(这在类库代码中更为重要,相比应用代码) (如果你需要这样的建议,参见Josh Bloch'sEffective Java, item 47.) 在优化Android程序时,会遇到的一个棘手问题是,保证你的程序能在不同的硬件平台上运行。虚拟机版本和处理器各部相同,因此运行在之上的速度也大不一样。但这并且不是简单的A比B快或慢,并能在设备间做出排列。特别的,模拟器上只能评测出一小部分设备上体现的东西。有无JIT的设备间也存在着巨大差异,在JIT设备上好的代码有时候会在无JIT的设备上表现的并不好。 如果你想知道一个程序在设备上的具体表现,就必须在上面进行测试。 避免创建不必要的对象 对象创建永远不会是免费的。每个线程的分代GC给零时对象分配一个地址池以降低分配开销,但往往内存分配比不分配需要的代价大。 如果在用户界面周期内分配对象,就会强制一个周期性的垃圾回收,给用户体验增加小小的停顿间隙。Gingerbread中提到的并发回收也许有用,但不必要的工作应当被避免的。 因此,应该避免不必要的对象创建。下面是几个例子: *如果有一个返回String的方法,并且他的返回值常常附加在一个StringBuffer上,改变声明和实现,让函数直接在其后面附加,而非创建一个短暂存在的零时变量。 *当从输入的数据集合中读取数据时,考虑返回原始数据的子串,而非新建一个拷贝.这样你虽然创建一个新的对象,但是他们共享该数据的char数组。(结果是即使仅仅使用原始输入的一部分,你也需要保证它的整体一直存在于内存中。) 一个更彻底的方案是将多维数组切割成平行一维数组: *Int类型的数组常有余Integer类型的。推而广之,两个平行的int数组要比一个(int,int)型的对象数组高效。这对于其他任何基本数据类型的组合都通用。 *如果需要实现一个容器来存放元组(Foo,Bar),两个平行数组Foo[],Bar[]会优于一个(Foo,Bar)对象的数组。(例外情况是:当你设计API给其他代码调用时,应用好的API设计来换取小的速度提升。但在自己的内部代码中,尽量尝试高效的实现。) 通常来讲,尽量避免创建短时零时对象.少的对象创建意味着低频的垃圾回收。而这对于用户体验产生直接的影响。 性能之谜 前一个版本的文档给出了好多误导人的主张,这里做一些澄清: 在没有JIT的设备上,调用方法所传递的对象采用具体的类型而非接口类型会更高效(比如,传递HashMapmap比Map map调用一个方法的开销小,尽管两个map都是HashMap).但这并不是两倍慢的情形,事实上,他们只相差6%,而有JIT时这两种调用的效率不相上下。 在没有JIT的设备上,缓存后的字段访问比直接访问快大概20%。而在有JIT的情况下,字段访问的代价等同于局部访问,因此这里不值得优化,除非你觉得他会让你的代码更易读(对于final ,static,及static final变量同样适用) 用静态代替虚拟 如果不需要访问某对象的字段,将方法设置为静态,调用会加速15%到20%。这也是一种好的做法,因为你可以从方法声明中看出调用该方法不需要更新此对象的状态。 避免内部的Getters/Setters 在源生语言像C++中,通常做法是用Getters(i=getCount())代替直接字段访问(i=mCount)。这是C++中一个好的习惯,因为编译器会内联这些访问,并且如果需要约束或者调试这些域的访问,你可以在任何时间添加代码。 而在Android中,这不是一个好的做法。虚方法调用的代价比直接字段访问高昂许多。通常根据面向对象语言的实践,在公共接口中使用Getters和Setters是有道理的,但在一个字段经常被访问的类中宜采用直接访问。 无JIT时,直接字段访问大约比调用getter访问快3倍。有JIT时(直接访问字段开销等同于局部变量访问),要快7倍。在Froyo版本中确实如此,但以后版本可能会在JIT中改进Getter方法的内联。 对常量使用Static Final修饰符 考虑下面类首的声明: 编译器会生成一个类初始化方法<clinit>,当该类初次被使用时执行,这个方法将42存入intVal中,并得到类文件字符串常量strVal的一个引用。当这些值在后面被引用时,他们通过字段查找进行访问。 我们改进实现,采用final关键字: 类不再需要<clinit>方法,因为常量通过静态字段初始化器进入dex文件中。引用intVal的代码,将直接调用整形值42;而访问strVal,也会采用相对开销较小的“字符串常量”(原文:“sring constant”)指令替代字段查找。(这种优化仅仅是针对基本数据类型和String类型常量的,而非任意的引用类型。但尽可能的将常量声明为static final是一种好的做法。 使用改进的For循环语法 改进for循环(有时被称为“for-each”循环)能够用于实现了iterable接口的集合类及数组中。在集合类中,迭代器让接口调用hasNext()和next()方法。在ArrayList中,手写的计数循环迭代要快3倍(无论有没有JIT),但其他集合类中,改进的for循环语法和迭代器具有相同的效率。 这里有一些迭代数组的实现: zero()是当中最慢的,因为对于这个遍历中的历次迭代,JIT并不能优化获取数组长度的开销。 One()稍快,将所有东西都放进局部变量中,避免了查找。但仅只有声明数组长度对性能改善有益。 Two()是在无JIT的设备上运行最快的,对于有JIT的设备则和one()不分上下。他采用了JDK1.5中的改进for循环语法。 结论:优先采用改进for循环,但在性能要求苛刻的ArrayList迭代中,考虑采用手写计数循环。 (参见Effective Javaitem 46.) 在私有内部内中,考虑用包访问权限替代私有访问权限 考虑下面的定义: 需要注意的关键是:我们定义的一个私有内部类(Foo$Inner),直接访问外部类中的一个私有方法和私有变量。这是合法的,代码也会打印出预期的“Value is 27”。 但问题是,虚拟机认为从Foo$Inner中直接访问Foo的私有成员是非法的,因为他们是两个不同的类,尽管Java语言允许内部类访问外部类的私有成员,但是通过编译器生成几个综合方法来桥接这些间隙的。 内部类会在外部类中任何需要访问mValue字段或调用doStuff方法的地方调用这些静态方法。这意味着这些代码将直接存取成员变量表现为通过存取器方法访问。之前提到过存取器访问如何比直接访问慢,这例子说明,某些语言约会定导致不可见的性能问题。 如果你在高性能的Hotspot中使用这些代码,可以通过声明被内部类访问的字段和成员为包访问权限,而非私有。但这也意味着这些字段会被其他处于同一个包中的类访问,因此在公共API中不宜采用。 合理利用浮点数 通常的经验是,在Android设备中,浮点数会比整型慢两倍,在缺少FPU和JIT的G1上对比有FPU和JIT的Nexus One中确实如此(两种设备间算术运算的绝对速度差大约是10倍) 从速度方面说,在现代硬件上,float和double之间没有任何不同。更广泛的讲,double大2倍。在台式机上,由于不存在空间问题,double的优先级高于float。 但即使是整型,有的芯片拥有硬件乘法,却缺少除法。这种情况下,整型除法和求模运算是通过软件实现的,就像当你设计Hash表,或是做大量的算术那样。 了解并使用类库 选择Library中的代码而非自己重写,除了通常的那些原因外,考虑到系统空闲时会用汇编代码调用来替代library方法,这可能比JIT中生成的等价的最好的Java代码还要好。典型的例子就是String.indexOf,Dalvik用内部内联来替代。同样的,System.arraycopy方法在有JIT的Nexus One上,自行编码的循环快9倍。 (参见Effective Javaitem 47.) 合理利用本地方法 本地方法并不是一定比Java高效。最起码,Java和native之间过渡的关联是有消耗的,而JIT并不能对此进行优化。当你分配本地资源时(本地堆上的内存,文件说明符等),往往很难实时的回收这些资源。同时你也需要在各种结构中编译你的代码(而非依赖JIT)。甚至可能需要针对相同的架构来编译出不同的版本:针对ARM处理器的GI编译的本地代码,并不能充分利用Nexus One上的ARM,而针对Nexus One上ARM编译的本地代码不能在G1的ARM上运行。 当你想部署程序到存在本地代码库的Android平台上时,本地代码才显得尤为有用,而并非为了Java应用程序的提速。 (参见Effective Javaitem 54.) 结语 最后:通常考虑的是:先确定存在问题,再进行优化。并且你知道当前系统的性能,否则无法衡量你进行尝试所得到的提升。 这份文档中的每个主张都有标准基准测试作为支持。你可以在code.google.com“dalvik”项目中找到基准测试的代码。 这个标准基准测试是建立在Caliper Java标准微基准测试框架之上的。标准微基准测试很难找到正确的路,所以Caliper帮你完成了其中的困难部分工作。并且当你会察觉到某些情况的测试结果并想象中的那样(虚拟机总是在优化你的代码的)。我们强烈推荐你用Caliper来运行你自己的标准微基准测试。 同时你也会发现Traceview对分析很有用,但必须了解,他目前是不不支持JIT的,这可能导致那些在JIT上可以胜出的代码运行超时。特别重要的,根据Taceview的数据作出更改后,请确保代码在没有Traceview时,确实跑的快了。 本文转自over140 51CTO博客,原文链接:http://blog.51cto.com/over140/616199,如需转载请自行联系原作者

优秀的个人博客,低调大师

Java并发编程实战系列11之性能与可伸缩性Performance and Scalability

线程可以充分发挥系统的处理能力,提高资源利用率。同时现有的线程可以提升系统响应性。 但是在安全性与极限性能上,我们首先需要保证的是安全性。 11.1 对性能的思考 提升性能=用更少的资源做更多的事情(太对了,这才是问题的本质)。 资源包括:CPU时钟周期,内存,网络带宽,I/O带宽,数据请求,磁盘空间等。 资源密集型说的就是对上述维度敏感的应用。 与单线程相比,多线程总会一起一些额外的性能开销: 线程协调with coordinating between threads (locking, signaling, and memory synchronization) 上下文切换increased context switching 线程创建和销毁thread creation and teardown 线程调度scheduling overhead 可伸缩性是指:增加资源,程序的吞吐可以成比例的增加。 性能的提高往往是一个权衡的过程,需要考虑诸多因素。 11.2 Amdahl定律 Amdahl's Law 收割可以靠并行提高性能,而作物生长则不行。这是一个很简单的自然界的问题,在计算机界也存在,需要对问题进行合理的分解,发现潜在的并行能力。 Amdahl定律:并行计算中的加速比是用并行前的执行速度和并行后的执行速度之比来表示的,它表示了在并行化之后的效率提升情况。 speedup <= 1 / F + (1 - F) /N F表示被串行化的部分,N表示处理器数量。 如果N无穷大,那么最大的加速比例是1/F。理论上如果50%是串行的,那么最大的加速比只能是2。如果10%串行。那么最大加速比接近10,如果N=10也就是说有10个处理器资源,那么最高的加速比是5.4,在100个处理器的情况下是9.2。 但是任何程序都存在串行部分,例如从队列中take数据,访问数据库的操作等,这是绝对的。 书中举了一个例子是Synchronized linkedlist和ConcurrentLinkedQueue的吞吐率对比,在处理器数量到达上限后,他们的吞吐都基本是一条持平的线,但是Synchronized linkedlist吞吐率更低,在处理器较少的情况下就到达了极限,这主要受context switch的限制。 11.3 线程引入的开销 单线程不存在线程调度,也不存在同步开销,不需要使用锁来保证安全一致性。而多线程这些都需要考虑。 11.3.1 上下文切换 操作系统的设计者巧妙地利用了时间片轮转的方式, CPU给每个任务都服务一定的时间, 然后把当前任务的状态保存下来, 在加载下一任务的状态后, 继续服务下一任务. 如果可运行的线程数大于CPU数量,那么OS会最终将某个正在运行的线程调度出来,从而让其他线程能够使用CPU,这会导致一次上下文切换,主要包括当前线程“保存现场”,并且新调度出来的线程需要“恢复现场“。这里的context switch直接消耗包括: CPU寄存器需要保存和加载, 系统调度器的代码需要执行, TLB实例需要重新加载, CPU 的pipeline需要刷掉; 间接消耗指的是多核的cache之间得共享数据, 间接消耗对于程序的影响要看线程工作区操作数据的大小). JVM和OS消耗的CPU时钟周期越少,那么APP可用的CPU时钟周期就越多。 往往OS有一个最小的执行时间,防止过于频繁的上下文切换。 JVM会因为阻塞比如锁、阻塞I/O而挂起线程,如果频繁的阻塞,就会无法使用完整的调度时间片。//? 如果可运行的线程数大于CPU的内核数,那么OS会根据一定的调度算法,强行切换正在运行的线程,从而使其它线程能够使用CPU周期。 切换线程会导致上下文切换。线程的调度会导致CPU需要在操作系统和进程间花费更多的时间片段,这样真正执行应用程序的时间就减少了。另外上下文切换也会导致缓存的频繁进出,对于一个刚被切换的线程来说,可能由于高速缓冲中没有数据而变得更慢,从而导致更多的IO开销。 vmstat 命令可以看cs这一个字段看上下文切换的数据。 11.3.2 内存同步 同步的性能开销包括多个方面。在synchronized和volatile提供的可见性保证中会使用一些特殊指令,即内存栅栏(memory barrier),内存栅栏可以刷新缓存,满足可见性,但是它也会抑制一些编译器优化,例如不能指令重排序。 现代的JVM对于无竞争的synchronized的消耗非常小,基本微乎其微。 同时现代的JVM编译优化做的非常成熟,一些不必要的同步开销往往可以优化掉。例如,下面的代码会去掉锁获取。 synchronized (new Object()) { // do something } 还有一些比如escape analysis会找出不会发布到堆上的本地对象,锁的获取和释放会被优化为最小的次数甚至去掉。例如下面的操作。 public String getStoogeNames() { List<String> stooges = new Vector<String>(); stooges.add("Moe"); stooges.add("Larry"); stooges.add("Curly"); return stooges.toString(); } 当然即使不escape,也会有lock coarsening过程,将临近的同步代码块使用同一个锁合并起来。这都减少了同步的开销。 所以不必过度担心非竞争同步带来的开销,这个基本的机制已经非常的快了,而且JVM还有能进行额外的优化以进一步降低或者消除开销的本领。 不同线程间要进行数据同步,synchronized以及volatile提供的可见性都会导致缓存失效。线程栈之间的数据要和主存进行同步,这些同步有一些小小的开销。如果线程间同时要进行数据同步,那么这些同步的线程可能都会受阻。 11.3.3 阻塞 竞争的同步需要OS介入,从而增加了开销。当在锁上发生竞争时,失败者线程会被阻塞,JVM在实现发现阻塞的行为时,可以采用 自旋等待 spin-waiting 或者OS挂起被阻塞的线程 这两种的效率高低取决于上下文切换的开销以及成功获取锁之前的等待时间,如果等待时间较短,则spin-waiting,如果较长则挂起。 一个线程被阻塞会产生上下文切换的影响,但是它到底何时执行这是由OS决定的,靠时间分片机制,这个调度的策略是OS解决的,而JVM的scheduler解决的是阻塞释放锁之后哪个线程需要被select出来执行,也就是转到runnable状态。 There is no single Java Virtual Machine; JVM is a specification, and there are multiple implementations of it, including the OpenJDK version and the Sun version of it, among others. I don't know for certain, but I would guess that any reasonable JVM would simply use the underlying threading mechanism provided by the OS, which would imply POSIX Threads (pthreads) on UNIX (Mac OS X, Linux, etc.) and would imply WIN32 threads on Windows. Typically, those systems use a round-robin strategy by default. Many types of algorithms exist like preemptive and time slicingwith round robin etc. The JVM is based on preemptive and priority based scheduling algorithm to select thread to run. 每个Java线程一对一映射到Solaris平台上的一个本地线程上,并将线程调度交由本地线程的调度程序。由于Java线程是与本地线程是一对一地绑在一起的,所以改变Java线程的优先权也不会有可靠地运行结果。 对于类Unix系统而言,一般都是进程作为任务的调度单位,也即是操作系统调度器,只会针对进程来分配CPU等资源。由于进程彼此独立,相互不可进行直接访问,这增加了应用的通信成本。所以后面有了微进程,微进程与进程不同的是,允许一定程度上,彼此可以直接进行访问,详细可参考LinuxThreads。JVM在一些类Unix平台下,就是将线程映射到操作系统的微进程,来实现线程调度。这样多线程能够直接被系统调度器进行调度,与此对应的就是其线程的创建和销毁的成本就比较高,而且JVM的线程优先级很难进行匹配,无法提供确切的保证,仅仅是个hint。 当发生锁竞争时,失败的线程会导致阻塞。通常阻塞的线程可能在JVM内部进行自旋等待,或者被操作系统挂起。自旋等待可能会导致更多的CPU切片浪费,而操作系统挂起则会导致更多的上下文切换。 11.4 减少锁的竞争 减少锁的竞争能够提高性能和可伸缩性。 在并发程序中,对可伸缩性的最主要的威胁就是独占方式的资源锁。 有三种方式可以减低锁的竞争程度: 减少锁的持有时间 降低锁的请求频率 使用带有协调机制的独占锁,这些机器允许更好的并发性。//? 11.4.1 缩小锁的范围(快进快出) 原理就是Amdah定律,串行的代码总量减少了。 11.4.2 减小锁的粒度 这种方式就是降低线程请求锁的频率,通过锁分解来实现。 下面的应用明显锁的粒度太粗了。 public class ServerStatusBeforeSplit { @GuardedBy("this") public final Set<String> users; @GuardedBy("this") public final Set<String> queries; public ServerStatusBeforeSplit() { users = new HashSet<String>(); queries = new HashSet<String>(); } public synchronized void addUser(String u) { users.add(u); } public synchronized void addQuery(String q) { queries.add(q); } public synchronized void removeUser(String u) { users.remove(u); } public synchronized void removeQuery(String q) { queries.remove(q); } } 锁分解就是独立的变量独立分配锁,不适用全局锁。优化后如下: public class ServerStatusAfterSplit { @GuardedBy("users") public final Set<String> users; @GuardedBy("queries") public final Set<String> queries; public ServerStatusAfterSplit() { users = new HashSet<String>(); queries = new HashSet<String>(); } public void addUser(String u) { synchronized (users) { users.add(u); } } public void addQuery(String q) { synchronized (queries) { queries.add(q); } } public void removeUser(String u) { synchronized (users) { users.remove(u); } } public void removeQuery(String q) { synchronized (users) { queries.remove(q); } } } 11.4.3 锁分段 最典型的例子就是ConcurrentHashMap。 public class StripedMap { // Synchronization policy: buckets[n] guarded by locks[n%N_LOCKS] private static final int N_LOCKS = 16; private final Node[] buckets; private final Object[] locks; private static class Node { Node next; Object key; Object value; } public StripedMap(int numBuckets) { buckets = new Node[numBuckets]; locks = new Object[N_LOCKS]; for (int i = 0; i < N_LOCKS; i++) locks[i] = new Object(); } private final int hash(Object key) { return Math.abs(key.hashCode() % buckets.length); } public Object get(Object key) { int hash = hash(key); synchronized (locks[hash % N_LOCKS]) { for (Node m = buckets[hash]; m != null; m = m.next) if (m.key.equals(key)) return m.value; } return null; } public void clear() { for (int i = 0; i < buckets.length; i++) { synchronized (locks[i % N_LOCKS]) { buckets[i] = null; } } } } 11.4.4 避免热点域hot field 比如HashMap的size方法,ConcurrentHashMap采用了牺牲size的准确性的策略。 11.4.5 一些替代独占锁的方法 ReadWriteLock,AtomicInteger,UNSAFE.compareAndSwap(..) 11.4.6 监测CPU的利用率 vmstat,kill -3 pid ”waiting to lock monitor…“有这句就证明竞争太激烈了。 11.5 示例:比较Map的性能 比较了ConcurrentHashMap和synchronized hashmap的性能对比。 串行访问Map一个锁 pk 多个线程能并发的访问Map通过分段锁。 竞争非常激烈的时候,synchronized hashmap伸缩性非常差,吞吐量不会随着线程数增加而增加,反而降低,因为每个操作消耗的时间大部分都用于上下文切换和调度延迟上了。 11.6 减少上下文切换的开销 举个例子,就是APP记录日志,例如写日志到本地或者远程RPC,直接记录会存在I/O阻塞,靠一个轻量级的queue来解耦,使得APP不感知影响,减少阻塞。 http://www.artima.com/insidejvm/ed2/threadsynch.html //TODO 总结 了解了性能的提升的几个方面,也了解性能的开销后,应用程序就要根据实际的场景进行取舍和评估。没有一劳永逸的优化方案,不断的进行小范围改进和调整是提高性能的有效手段。当前一些大的架构调整也会导致较大的性能的提升。 性能提升考虑的方面: 系统平台的资源利用率 一个程序对系统平台的资源利用率是指某一个设备繁忙且服务于此程序的时间占所有时间的比率。从物理学的角度讲类似于有用功的比率。简单的说就是:资源利用率=有效繁忙时间/总耗费时间。 也就说尽可能的让设备做有用的功,同时榨取其最大值。无用的循环可能会导致CPU 100%的使用率,但不一定是有效的工作。有效性通常难以衡量,通常只能以主观来评估,或者通过被优化的程序的行为来判断是否提高了有效性。 延迟 延迟描述的是完成任务所耗费的时间。延迟有时候也成为响应时间。如果有多个并行的操作,那么延迟取决于耗费时间最大的任务。 多处理 多处理是指在单一系统上同时执行多个进程或者多个程序的能力。多处理能力的好处是可以提高吞吐量。多处理可以有效利用多核CPU的资源。 多线程 多线程描述的是同一个地址空间内同时执行多个线程的过程。这些线程都有不同的执行路径和不同的栈结构。我们说的并发性更多的是指针对线程。 并发性 同时执行多个程序或者任务称之为并发。单程序内的多任务处理或者多程序间的多任务处理都认为是并发。 吞吐量 吞吐量衡量系统在单位之间内可以完成的工作总量。对于硬件系统而言,吞吐量是物理介质的上限。在没有达到物理介质之前,提高系统的吞吐量也可以大幅度改进性能。同时吞吐量也是衡量性能的一个指标。 瓶颈 程序运行过程中性能最差的地方。通常而言,串行的IO、磁盘IO、内存单元分配、网络IO等都可能造成瓶颈。某些使用太频繁的算法也有可能成为瓶颈。 可扩展性 这里的可扩展性主要是指程序或系统通过增加可使用的资源而增加性能的能力。

优秀的个人博客,低调大师

理解 OpenStack Swift (3):监控和一些影响性能的因素 [Monitoring and Performance]

本系列文章着重学习和研究OpenStack Swift,包括环境搭建、原理、架构、监控和性能等。 (1)OpenStack + 三节点Swift 集群+ HAProxy + UCARP 安装和配置 (2)原理、架构和性能 (3)监控 对 Swift 集群的监控是必要的,特别是集群规模很大的时候。 1. 监控目标 主要的监控目标包括: 硬件故障 操作系统故障 Swift集群健康状态 Swift 集群状态 2. Swift 提供的工具 2.1 Swift 自带的各种 Audit 工具 2.1.1 磁盘监控工具swift-drive-audit 该工具分析/var/log/kern.log 文件,根据预定义的 regexp 来探测 kernel 报告的磁盘错误。通常地它会被 cron 周期性地运行。它使用一个配置文件,比如/etc/swift/drive-audit.conf。如果该脚本发现了某个磁盘存在问题,它会自动 unmount 它,而且会在/etc/fstab 中将它注释掉。然后后端 replication 进程就会从其它 replica 中拷贝出一个新的 replica。 这是示例配置文件。 要使用它,必须首先创建一个配置文件 driver-audit.conf: [drive-audit] device_dir = /srv/node log_facility = LOG_LOCAL0 log_level = INFO minutes = 60 error_limit = 1 log_file_pattern = /var/log/kern* regex_pattern_1 = \berror\b.*\b(dm-[0-9]{1,2}\d?)\b 然后运行该工具:swift-drive-auditdriver-audit.conf,然后上面描述的各种 action 就会有了。 2.1.2 account,container 和 object auditor 功能: swift-account-auditor 会打开 account server 的 sqlite db,运行 SQL 查询,确保 db 是有效的,并报告一个account 是否有 replica 缺失或者错误的对象。 swift-container-auditor 会对 containers 做同样的事情. swift-object-auditor 会打开一个对象服务器上的所有对象,确保它们的元数据是正确的,而且有有合适的大小和 MD5 它们都必须在各自对应的服务的配置文件中的 [account/object/container-auditor] 部分做相应配置,然后它们就会定期运行,并输出日志。 2.2 集群监控状态获取工具swift-dispersion-populate 和 swift-dispersion-report 这两个工具用于获取和报告集群的总体健康状态。它需要访问Swift 集群以及 Ring 文件。它需要一个自己的配置文件,往往把该配置文件放在 proxy server 上的 /etc/swift 目录下。 (1)创建配置文件 /etc/swift/dispersion.conf : [dispersion] auth_url = http://controller:35357/v3 auth_user = service:swift auth_version = 3 auth_key = 1111 swift_dir = /etc/swift concurrency = 25 retries = 5 该配置文件的详细说明可以参见Ubuntu 文档。注意 Kilo 版本的 Swift 环境中需要添加配置项 auth_version。 (2)运行swift-dispersion-populate 去获取系统的健康状况,它和 swift-dispersion-report使用同一个配置文件。详细说明可以参见Ubuntu 文档。 root@swift1:/etc/swift# swift-dispersion-populate dispersion.conf Created 10 containers for dispersion reporting, 0s, 0 retries Created 10 objects for dispersion reporting, 0s, 0 retries (3)运行swift-dispersion-report 获取监控报告。详细说明请参加Ubuntu 文档。 root@swift1:/etc/swift# swift-dispersion-report Queried 11 containers for dispersion reporting, 0s, 0 retries 100.00% of container copies found (33 of 33) Sample represents 1.07% of the container partition space Queried 10 objects for dispersion reporting, 0s, 0 retries There were 10 partitions missing 0 copy. 100.00% of object copies found (30 of 30) Sample represents 0.98% of the object partition space 2.3 性能获取工具swift-recon 中间件 2.3.1 它是什么(what) Swift Recon是一个安装在对象服务器的pipeline上的中间件,它有一个必填选项:一个本地缓存目录。它可以获取: How many unmounted (failed) drives there are in the cluster, and on which servers those are located How many async pendings are present Drive usage and balance Load Average (for easy access later on*) Memory Usage (for easy access later on*) Checking ring md5sum’s Logged replication stats Connection stats (tbd) Quarantine Statistics (a new pending addition) 要使用它,需要首先修改 object-server 的配置文件,添加该中间件: [pipeline:main] pipeline = healthcheck recon object-server [filter:recon] use = egg:swift#recon recon_cache_path = /var/cache/swift recon_lock_path = /var/lock 2.3.2 使用命令行工具(How) swift-recon <server_type> [-v] [--suppress] [-a] [-r] [-u] [-d] [-l] [--md5] [--auditor] [--updater] [--expirer] [--sockstat] 参数: -a, --async: Get async stats --auditor: Get auditor stats --updater: Get updater stats --expirer: Get expirer stats -r, --replication: Get replication stats -u, --unmounted: Check cluster for unmounted devices -d, --diskusage: Get disk usage stats -l, --loadstats: Get cluster load average stats -q, --quarantined: Get cluster quarantine stats --md5 Get md5sum of servers ring and compare to local cop --all Perform all checks. Equivalent to -arudlq --md5 -z ZONE, --zone=ZONE: Only query servers in specified zone --swiftdir=PATH Default = /etc/swift (1)获取全部zone 上 updater,auditor 和 expier 的状态 root@swift1:/etc/swift# swift-recon --auditor --updater --expirer =============================================================================== --> Starting reconnaissance on 3 hosts =============================================================================== [2015-11-14 16:52:00] Checking auditor stats [ALL_audit_time_last_path] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 3 [ALL_quarantined_last_path] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 3 [ALL_errors_last_path] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 3 [ALL_passes_last_path] low: 1, high: 1, avg: 1.0, total: 3, Failed: 0.0%, no_result: 0, reported: 3 [ALL_bytes_processed_last_path] low: 5, high: 5, avg: 5.0, total: 15, Failed: 0.0%, no_result: 0, reported: 3 [ZBF_audit_time_last_path] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 3 [ZBF_quarantined_last_path] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 3 [ZBF_errors_last_path] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 3 [ZBF_bytes_processed_last_path] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 3 =============================================================================== [2015-11-14 16:52:00] Checking updater times [updater_last_sweep] low: 0, high: 0, avg: 0.1, total: 0, Failed: 0.0%, no_result: 0, reported: 3 =============================================================================== [2015-11-14 16:52:00] Checking on expirers [object_expiration_pass] - No hosts returned valid data. [expired_last_pass] - No hosts returned valid data. =============================================================================== (2)磁盘使用情况 root@swift1:/etc/swift# swift-recon -d =============================================================================== --> Starting reconnaissance on 3 hosts =============================================================================== [2015-11-14 16:53:38] Checking disk usage now Distribution Graph: 2% 2 ********************************************** 3% 3 ********************************************************************* 4% 1 *********************** Disk usage: space used: 5343854592 of 160982630400 Disk usage: space free: 155638775808 of 160982630400 Disk usage: lowest: 2.59%, highest: 4.56%, avg: 3.31952247191% (3)系统负载 root@swift1:/etc/swift# swift-recon -l =============================================================================== --> Starting reconnaissance on 3 hosts =============================================================================== [2015-11-14 16:54:32] Checking load averages [5m_load_avg] low: 0, high: 0, avg: 0.1, total: 0, Failed: 0.0%, no_result: 0, reported: 3 [15m_load_avg] low: 0, high: 0, avg: 0.1, total: 0, Failed: 0.0%, no_result: 0, reported: 3 [1m_load_avg] low: 0, high: 0, avg: 0.1, total: 0, Failed: 0.0%, no_result: 0, reported: 3 (4)检查所有节点上的 ring 文件和 swift.conf 是不是完全一致 root@swift1:/etc/swift# swift-recon --md5 =============================================================================== --> Starting reconnaissance on 3 hosts =============================================================================== [2015-11-14 17:08:38] Checking ring md5sums 3/3 hosts matched, 0 error[s] while checking hosts. =============================================================================== [2015-11-14 17:08:38] Checking swift.conf md5sum 3/3 hosts matched, 0 error[s] while checking hosts 2.3.2 通过 REST API 使用 URL 及功能列表: 3. 其它监控工具 Swift 自身带的监控工具能提供比较丰富的功能,但是使用起来还不是很方便,因此,业界已经出现了很多商业的或者开源的监控工具。这些监控工具,要么向 swift-recon 一样实现了一个新的中间件,要么调用 Swift 自身的工具提供的接口。以下(1 - 4 部分)文字引用自OpenStack Object Storage Monitoring一文: (1)Swift-Informant Florian Hines 开发的 Swift-Informant 中间件可以获得 OS 客户请求的实时结果。它位于proxy server的 pipeline,在每一个请求到达Proxy server之后,它发送3类统计数据到StatsD server. 一个类似obj.GET.200 or cont.PUT.404的增量计数器。 请求处理的时间长度 传输数据数量 这有利于了解客户正体验的服务品质,也可以了解各种服务类型,命令以及响应码的不同排列的数量。Swift-Informant也要求no change to core Swift code因为它是用中间件实现的。然而,也因为如此,它不能让人看到代理服务器后面的工作情景。如果一个存储节点的响应降低,你也可能看到你的请求变差-要么是高延迟,要么返回错误状态码。你不会知道具体是为什么,或者请求是要去哪里。或许你所考虚的container服务器是在一个好的节点的,而对象服务器是在另一个性能差的节点。 因此我们需要深入视觉到proxy server的后面,集群的操作里去。 (2)Statsdlog Florian 的 statsdlog 工程增加了 StatsD 基于日志事件的计数器。正如Swift-Informant,它也是非侵入式的。但是statsdlog可以从Swift后台进程中追踪事件,而不仅仅是代理服务器。后台进程监听syslog信息的UDP流,当一条日志匹配于一个正则表达式时,StatsD计数器递增。Metric名字映射到JSON文件中的正则pattern。JSON文件允许灵活配置以能从日志流中抽取出想要的metrics。 (3)Swift StatsD Logging StatsD 使用程序代码来作深入分析。Metrics被有针对性的代码实时发送。发送一个metric的开销相当低:一个sendto UDP包。如果你认为这样的开销还算高的话,StatsD client library可以只发送样本的随机比率部分,flushing metrics upstream时StatsD将估算实际数据。要避免基于中间件的监控与事后日志处理所带来的弊端,我们把StatsD metrics发送过种整合到Swift本身。我们当前所提交的更改报告了遍布15个swift后台进程的124个metrics以及tempauth中间件。metrics细节可参考https://review.openstack.org/#patch,sidebyside,6058,2,doc/source/admin_guide.rst (4)总结 我们认为 Swift 集群动作最好的监控方法是综合了一个通用服务器监控系统,一个Swift特定统计指标收集(polling Swift-specific gauge metrics)机制,一个Swift内部counter与timing metrics深度StatsD日志手段的结合体。对于polling Swift-specific gauge metrics,最好使用一个通用的收集插件。这个插件即可以从swift-recon读数据,也可以自己直接收集信息。在 SwiftStack,我们使用 collectd 加上一些服务器监控用 python 插件代码。我们也在collectd中嵌入StatsD服务器,这样每个节点都有一个进程将stats数据“倒”给Graphite"(http://graphite.wikidot.com/)集群。有了这个装置,我们拥有前述所有问题的全覆盖解决方案:general purpose monitoring, Swift-specific gauge monitoring, and real-time counter and timing data directly from Swift。除了图形化,你也可以实现异常检测,触发警告,维护一个实时的实体健康状态视图,避免各种突发问题。 (5)SwiftStack 监控工具的部分截图: (6)另外一个监控环境示例 (7)Benchmarking 工具 Intel 开源了对象存储的 Benchmarking 工具 COSBench,https://github.com/intel-cloud/cosbench 4. 一些影响性能的因素 注:以下内容引用自 "Leveraging open source tools to gain insight into OpenStack Swift“ ,byMichael Factor,Dmitry Sotnikov, dmitrys@il.ibm.com。他们使用的测试环境: 4.1 Container 数量和 Swift 版本 (PUT 操作) 版本和 container 数目带来的性能差异还是非常大的。建议使用 Swift 2.2 版本以上,以及多个 container。 4.2 客户端 worker 数目 可见客户端 worker 数目不是越多性能就会线性增加。 4.3 Container 中的对象数目 4.4 前端和后端网络带宽 说明后端网络的带宽压力是前端的至少3倍。 4.5 存储节点的磁盘I/O 和前端网络 I/O 的关系(12倍) 4.6 对象大小 这说明 Swift 不合适处理大量的小文件。 4.7 IOPS 性能不是随着存储节点数目线性扩展的 也就是说这个环境中,存储节点数目为7的话,它们就不是 IOPS 性能瓶颈了。 参考文档: http://www.cnblogs.com/Clisa/p/3461701.htmlhttps://swiftstack.com/blog/2012/04/11/swift-monitoring-with-statsd/https://platform.swiftstack.com/docs/admin/monitoring/cluster-monitoring.html http://blog.chmouel.com/2012/02/01/audit-a-swift-cluster/

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册