《架构师》期刊摘要(2014年)

1、WikiPedia上说“DevOps是软件开发、运维和质量保证三个部门之间的沟通、协作和集成所采用的流程、方法和体系的一个集合。它是人们为了及时生产软件产品或服务,以满足某个业务目标,对开发和运维之间相互依存关系的一种新的理解。”这恰好体现了精益管理中的客户价值原则,即:客户的关键来确定企业从设计到生产交付的全部过程,实现客户需求的最大满足。我们也可以把DevOps看作是一种能力,在缺乏这种能力的组织中,开发和运维之间存在着信息“鸿沟”。

    如何获取这种能力呢?关键有两点:一是全局观,要从软件交付的全局出发,加强各角色之间的合作;二是自动化,人机交互界面的强大管理工具,比如各种受版本控制的script,以及类似于Nagios这样的基础设施监控工具,类似于Puppet、Chef这样的基础设计配置管理工具。

--《DevOps:不是传说》,作者:乔梁(2014年一月)

2、

    一张白纸上有一个墨点。

    观察者在纸外,目光被墨点所吸引;从业者生活在纸上,大部分世界仍然是白色的。

    我们对一项新技术或者新产品的真实被接受程度总会产生偏高的误判,而对老技术和老产品的被接受程度则产生偏低的误判。

    最后,之说以说“贴近正确”,是因为我不认为有绝对的正确。一个世界从不同的角度去看,总是不同的;即使尝试穷尽所有的观察角度来完整的理解一个世界,也是不可行的,因为世界总是在变化,而一个观察者在一个时间点只能完成一个角度的观察,当换一个观察角度的时候,他看到的已经不再是原来的世界。

    所有的观察者都存在偏见,但我们会尽力让他贴近真实。

--序言部分(2014年二月)

3、一名云开发人员就是负责这样的代码解决方案的人:解决方案是基于水平扩展的、分布式的、幂等的和异步处理,同时具有可伸缩、高度可用和弹性存储的特点。

4、性能无非就是两点-吞吐率(比如单位/秒)和响应时间(有时也成为等待时间)。最重要的是给出量化的指标,而不能只是说它应该“很快”。

    对于交易系统来说,实施系统更倾向于另外一种含义,那就是系统必须拥有高吞吐率并且尽快响应每个事件,这可以理解为“低延迟”。

    JAVA最大的弱点就是缺少对内存曾的控制。在主流处理器中一次缓存未命中就会丢失500条已经执行过的指令。为了避免缓存不能命中,我们需要控制内存曾,以一种可预测的方式访问内存。为了达到这种程度的控制,并减轻垃圾回收的压力,我们通常要用DirectByteBuffers去创建数据结构,或者放弃堆而使用Unsafe。这就可以做到精确的数据结构规划。如果Java引入对结构体数组的支持,就不必要再这儿做了。这并不需要切换语言,只是引入一些新的特性。

    对于低延迟应用来说,锁竞争可能是最大的一个性能障碍了。锁本身并没有性能开销,在无竞争情况下Java的同步锁也可以执行的非常好。然而,有竞争时锁的性能就会一落千丈,不仅仅因为一个线程持有锁使其他线程拿不到同一个锁,还因为更多的线程访问这个锁时会给JVM带来昂贵的锁管理成本,这个道理很容易理解。很明显,关键是要避免锁竞争,所以不要同步那些不需要同步的东西(比如,移除那些什么都不需保护的锁、缩小锁的范围、降低锁持有的时间、不要混淆锁的职能等等)。另一种常用的技术是消除多线程访问,不要让多个线程去访问一个共享的数据结构;你可以把更新操作当成命令排成队列,这样就变成单线程处理。这样就把锁竞争简单的归结成了添加在队列中的一个项目了,而它可以通过锁无关技术(无锁并发)来实现自我的管理。

--《虚拟研讨会:在低延迟环境中使用JAVA》(2014年8月)

5、高性能的三个主题:

    1)传输:用什么样的通道将数据发送给对方,BIO、NIO或者AIO,IO模型在很大程度上决定了框架的性能。

    2)协议:采用什么样的通信协议,HTTP或者内部私有协议。协议的选择不同,性能模型也不同。相比于公有协议,内部私有协议的性能通常可以被设计的更优。(补充:比如Protobuf比JAVA内部的序列化更加高效)

    3)线程:数据包如何读取?读取之后的编码在哪个线程进行,编码后的消息如何派发,Reactor线程模型的不同,对性能的影响也非常大。

    IO多路复用技术通过把多个IO的阻塞复用到同一个select的阻塞上(NIO + Selector),从而使得系统在单线程的情况下可以同时处理多个客户端请求。与传统的多线程、多进程模型比,IO多路复用的最大优势是系统开销小,系统不需要创建新的额外的进程或者线程,也不需要维护这些进程和线程的运行,降低了系统的维护工作量,节省了系统资源。

    1)异步处理模型:NIO、多个Selector,Reactor线程模型。

    2)零拷贝:Netty接收和发送ByteBuffer采用Direct Buffers,即使用堆外内存进行Socket读写,不需要进行字节缓冲区的二次Copy;如果使用传统的堆内存(Heap buffer),JVM会将堆内存Buffer拷贝一份到直接内存中,然后才进行Socket写入(Netty提供了DirectBuffer和HeapBuffer两种Buffer机制)。Netty还可以将多个Buffer对象进行组合操作,用户可以操作多个Buffers像操作一个buffer一样(scatter/gather),避免了传统通过内存拷贝的方式将几个小buffer合并成一个大buffer。此外,Netty的文件传输采用的transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致内存拷贝的问题(sentfile)。

    3)内存池(PooledBuffer):heap buffer的创建于回收相对简单,但是对direct buffer的分配和回收,是一件耗时的操作(OS调用)。为了尽量重用缓冲区,Netty提供了基于内存池的缓冲区重用机制。测试结果显示,PooledBuffer机制比普通的Buffer(非重用)相比,性能提升了20余倍。

    Java的优雅退出通常通过注册JDK的shutdownHook来实现,当系统接收到到退出指令后,首先标记系统处于退出状态,不再接受新的消息,然后将挤压的消息处理完,然后调用资源回收接口将资源销毁,最后各线程退出执行。Netty的Reactor线程(组)提供了优雅退出的方式,在EventExecutorGroup中shutdownGracefully()方法。(子类包括我们常用的NioEventLoopGroup)。

    Netty提供了流量整形的特性(tranffic shaping),即对流量的速速率进行限定的操作,对于超过阈值的流量暂且缓存起来,当面对高流量输入业务时,最终确保流量以相对安全的速度传递给接收端;在本质上与流量控制(Flow Control)有较大的相似之处,区别就是流量控制通常采用“漏桶模型”,对超过阈值的流量直接拒绝或者抛弃,而流量整形只是暂缓发送。基本原理:对每次读取到的ByteBuffer可写字节数进行计算,获取当前的报文流量,然后与流量整形阈值对比。如果已经达到或者超过阈值,则计算等待时间delay,将当前的ByteBuffer放到定时任务Task中缓存,由定时任务线程池在延迟delay之后继续处理该byteBuff。(参见ChannelTrafficShapingHanlder)

--《专题:Netty之道》(李林锋)

6、

    1、使用线程池的意义:

    1)复用:类似于WEB服务器等系统,长期来看内部需要使用大量的线程池处理请求,而单次请求乡音时间通常比较短,此时JAVA基于操作系统的本地调用方式大量的创建和销毁线程本身就会成为系统的一个性能瓶颈和资源浪费。若使用线程池技术可以实现工作线程的复用,即一个工作线程创建和销毁的生命周期期间可以执行处理多个任务,从而总体上降低线程创建和销毁的频率和时间,提升系统的性能。

    2)流控:服务器资源有限,超过服务器性能的过高并发设置反而成为系统的负担,造成CPU大量消耗于上下文切换、内存溢出等后果。通过线程池技术可以控制系统最大并发数和最大任务处理量,从而很好的实现流控,保证系统不至于崩溃。

    3)功能:JDK的线程池实现非常灵活,并提供了很多功能,一些场景基于功能的角度会选择线程池。

    2、线程池技术要点:

    1)工作者线程worker:通常称为worker线程,即可以重用并执行多个任务的线程,一个worker线程的生命周期内会不停的处理多个业务job。线程池“复用”的本质就是使用一个worker线程去处理多个job,“流控”的本质就是通过对worker数量的控制实现并发数的控制。

    2)待处理工作job的存储队列:工作者线程workers的数量是有限的,同一时间最多只能处理workers线程数量的任务。对于来不及处理的的job需要保存在等到队列中(pending queue),此后空闲的worker线程即可不停的从job等待队列中获取任务并执行。基于线程池队列的不同实现,可以扩展出多种功能的线程池,比如FIFO、基于权重排序的、有界队列等等。

    3)线程池初始化:即线程池参数的设定和多个workers的初始化,通常在初始化线程池时就需要指定workers线程的个数。

    4)处理业务job算法:...

    5)workers的增加算法:业务线程数部署持久不变的,有高低峰期。线程池要有自己的算法根据业务请求的频率高低调节自身工作者workers的数量来调节线程池的大小,从而实现业务高峰期增加工作者数量提高响应速度,而业务低峰期减少工作数量节约服务器资源。增减算法通常根据“待处理工作job数”、“线程池定义的max、min工作线程数”、“worker空闲时间”等。

    6)线程池终止逻辑:线程池被终止时,需要有一定的保障策略,gracefully,这些策略需要确保worker线程平滑退出、job队列是否被清除等。

--《几种线程池的实现算法分析》(2014年8月)

相关推荐