from:  http://blog.csdn.net/jacktan/article/details/41177779

在很久很久以前,如果有人让我用Java语言开发一个低延迟系统,我肯定会用迷茫的眼神望着他,然后说“are you kidding me?”。然而随着Java语言的日臻完善以及JVM性能的极速提升,使得用Java语言开发低延迟(不要和实时系统搞混)系统越来越成为可能,其中就包括最典型的交易(支付)系统。当然作为系统架构师,他们会尝试使用一些成熟分布式架构方案(通常是整合一些商业或开源项目),通过利用冗余计算资源以及异步通信方式提高应用程序的吞吐量和响应率,使其到达低延迟系统的标准,这在社区中有大量的实践案例,包括淘宝,京东、XX系等。然而我的兴趣爱好是研究Java语言本身能为低延迟应用开发带来什么,在开发低延迟系统中我们有哪些实践可以参照,这才是本文的讨论重点。关于低延迟系统和实时系统的区别不再赘述,作为架构师的你们应该比我清楚的多。

作为低延迟系统,比如交易系统,应该有2个比较重要的参数指标:吞吐量和响应率(当然还有其他重要指标)。吞吐量表达了系统在单位时间内所处理的请求量;而响应率则表达了单次请求所消耗的单位时间。这2个指标基本能判断出一个交易系统是否“足够快”。当我们在使用Java语言开发低延迟系统时,应该放弃一些我们之前约定俗成的规则,其中就包括了我们一直信奉的Java编程原则——面向对象。有人肯定会说我,“这不是扯蛋吗?那你还用Java干啥?”。其实我们并不会放弃Java面向对象的思维方式,而是在使用的方式上有所改变而已。Java设计之初就是纯面向对象的,记得之前所有Java入门书中都会有一句名言:“在Java世界,一切皆对象!”。有点跑题了,写这篇文章也是因为之前看到了一篇文章《Using Java in Low Latency Environments》,在这篇文章中几位大师讨论了有关于Java在低延迟环境中的使用方法,有些原则非常值得参考,再结合自己实践工作中的一些经验的积累,所以总结了三条最最重要的基本准则,以供同学们参考。

如果你是一个Java老手,肯定对JVM或是Java语言的各种特性了如指掌。JVM的内存释放是由GC自动完成的,程序员无法直接控制和干预GC的执行(有人会说,不是有System.gc()可以执行垃圾收集嘛,那就请你好好的去看一下Java Doc吧),这也是我对Java最大的诟病之一。我们都知道,当JVM在执行GC时,不管是YGC还是FULL GC,JVM都将阻塞其他所有正在执行的线程,虽然这个时间已经从分钟级别降低到了毫秒级别,但是作为低延迟系统还是会受其影响,从而降低系统的响应率。这种情况直到Oracle推出带有并行GC的JVM之前都会一直存在,为了避免这种情况,大师们的解决方案是降低GC的频率,将GC控制在每天一次或是几天一次,那到底这么做呢?大师们为我们指明了一条明路。那就是环保——尽量少产生“垃圾”或不产生“垃圾”,简单讲就是少使用堆对象(用new关键字实例化的对象),甚至包括String对象。好吧,小伙伴们都惊呆了,你是要我去写C代码嘛!!还好,我会C不会因此而失业——开个玩笑。其实大师们想表达的意思是对象复用技术,这种技术可以大量减少堆对象的产生。在我现在的交易系统开发中,基本不会关注对象的复用,字符串对象更是当做了基本类型来使用。其实作为交易系统,业务逻辑非常的复杂,各种逻辑判断,上百个交易业务属性,再加上对面向对象技术以及设计模型的迷恋,势必会引起堆对象的泛滥从而导致GC的频繁执行。所以为了减少“垃圾”的产生,我们必须在对象的设计和使用上做一些约束,例如,用基本类型(short、int、long、double等)替换包装对象、减小对象的规模(不要嵌套对象太多)、用数组替换Java集合、使用对象池复用对象(例如:commons-pool库)、减少第三方类库的使用等等。当然我们所做的一切都比不上来自GC自身的改进,所以真心希望oracle尽快的推出可以并行的垃圾收集器,使其不再成为我们既爱又恨的关注点。

其次对低延迟系统具有影响的就是Java的内存模型,即JMM。Java内存模型定义了可见性和原子性,为了保证这两项实现,我们必须使用同步,在Java中,所有线程的同步必须争夺唯一的一把锁,因此在需要低延迟的环境中锁竞争会大大的影响吞吐量和响应率。在交易系统中,当请求量急剧上升时,锁的竞争将更加的激烈,从而导致大量的线程阻塞或是饿死。那如何来规避这种情况的发生呢?那就是使用无锁技术或无等待技术,具体而言就是在同步块中不加锁或是减小加锁的代码范围。我们可以避免使用synchronized或是使用ReentrantLock来自己控制锁的范围。ReentrantLock允许我们在代码块上加锁,但是必须要注意不要忘记释放锁。举一个例子,假设我们的交易系统中有一个共享的数据,每个写请求方法都需要用synchronized关键字加以同步,否则就会发生数据异常。现在我们用无锁技术来规避synchronized,实现很简单,就是使用一个队列,将所有写请求先放入队列中,然后由一个线程循环队列,将写请求写入共享数据中。其实这就是我们常说的“单一写原则”,另外异步处理也是一种“单一写原则”的具体化实现。

IO可以说是影响低延迟系统性能最为关键的因素之一,而网络IO更是各种IO调用的重中之重。在交易系统中网络IO无法避免,我们必须通过以太网从其他应用程序中获取资源,比如:数据库,消息系统等,同时我们又会通过以太网向其他应用系统输出服务,比如:交易通知等。所以,网络质量将直接影响到交易系统的吞吐量和响应时间。在广域网环境中,网络传输需要时间、为了保证TCP/IP可靠协议必须重新发送丢失的数据包,交换机或路由器也会产生网络阻塞,这些完全不可预知的问题都将影响到低延迟系统的性能,IO的延迟无时无刻的在考验着我们的忍耐底线。到目前为止,我们还没有一个绝对可行的方案来解决所有由IO引起的问题,但还是有一些指导建议值得我们去借鉴。我们可以通过预加载资源来最大限度的减少IO开销,例如,在应用程序启动的时候加载配置文件或其他资源文件等。这里需要注意的是,加载的资源不能在应用程序中被垃圾回收,让其存在于堆内存的P区中是一个不错的选择。另一方面,从JDK7开始,Java提供了SDP的支持,SDP协议可以大大的提升网络IO的性能,所谓SDP就是Sockets Direct Protocol,即套接字直联协议。它不同于传统TCP/IP协议,它需要硬件的支持,即InfiniBand网络设备。SDP可以直接访问远程主机的内存,不再需要通过ISO的7层模型来进行数据的传输,所以它的效率要比以太网的TCP/IP协议高很多。我们用一张图就可以非常清楚的对比SDP协议和以太网协议的本质区别.。(此图从infoq上摘录,非本人版权,特此声明)

从上图中可以看到,Java7提供的SDP协议是直接和物理层打交道,数据不再像之前的Java6那种以太网的方式要经过ISO的各层。当然,对于开发人员来说这一切都是是完全透明的,我们还是在使用非常熟悉的java.net.*包中各种API进行网络应用程序的开发,所有的一切全部交给JDK。是不是觉得很Cool呢!不过本人还未对此进行过尝试,因为我们公司目前还是以太网,并没有InfiniBand网络设备,所以SDP技术还有待验证。

综上所述,在用Java做低延迟系统开发时,我们应该从三个方面着手制定优化方案,第一,有效减少垃圾收集的执行频率;第二,有效的使用锁机制或根本不用锁;第三,减少IO(重点是网络IO)的等待处理时间。当然除此之外还有一些其他的小技巧,比如,不使用第三方库、不使用反射库(java.lang.reflect)、优化代码执行路径、用DirectByteBuffers创建数据结构等等。

好像写了那么多自我感觉干货不是太多,其实我只是想抛砖引玉,通过这样一篇文章能够激发出更多的碰撞和辩论,低延迟系统的开发是一个大课题,其复杂程度远远超过本文所讲述的内容,所以希望更多的人能参与进来,把砖头扔向我。

转: 低延迟系统的Java实践的更多相关文章

  1. 构建数据湖上低延迟数据 Pipeline 的实践

    T 摘要 · 云原生与数据湖是当今大数据领域最热的 2 个话题,本文着重从为什么传统数仓 无法满足业务需求? 为何需要建设数据湖?数据湖整体技术架构.Apache Hudi 存储模式与视图.如何解决冷 ...

  2. 高吞吐、低延迟 Java 应用的 GC 优化实践

    本篇原文作者是 LinkedIn 的 Swapnil Ghike,这篇文章讲述了 LinkedIn 的 Feed 产品的 GC 优化过程,虽然文章写作于 April 8, 2014,但其中的很多内容和 ...

  3. 大型网站系统与Java中间件实践

    大型网站系统与Java中间件实践(贯通分布式高并发高数据高访问量网站架构与实现之权威著作,九大一线互联网公司CTO联合推荐) 曾宪杰 著   ISBN 978-7-121-22761-5 2014年4 ...

  4. java 11 ZGC(可伸缩,低延迟的gc)

    ZGC, A Scalable Low-Latency Garbage Collector(Experimental) 可伸缩,低延迟的gc ZGC, 这应该是JDK11最为瞩目的特性, 没有之一. ...

  5. 高吞吐低延迟Java应用的垃圾回收优化

    高吞吐低延迟Java应用的垃圾回收优化 高性能应用构成了现代网络的支柱.LinkedIn有许多内部高吞吐量服务来满足每秒数千次的用户请求.要优化用户体验,低延迟地响应这些请求非常重要. 比如说,用户经 ...

  6. 系统间通信(5)——IO通信模型和JAVA实践 下篇

    7.异步IO 上面两篇文章中,我们分别讲解了阻塞式同步IO.非阻塞式同步IO.多路复用IO 这三种IO模型,以及JAVA对于这三种IO模型的支持.重点说明了IO模型是由操作系统提供支持,且这三种IO模 ...

  7. 《大型网站系统与JAVA中间件实践》【PDF】下载

    <大型网站系统与JAVA中间件实践>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062557 内容简介 到底是本什么书,拥有这样 ...

  8. Linux低延迟服务器系统调优

    最近做了一些系统和网络调优相关的测试,达到了期望的效果,有些感悟.同时,我也发现知乎上对Linux服务器低延迟技术的讨论比较欠缺(满嘴高并发现象):或者对现今cpu + 网卡的低延迟潜力认识不足(动辄 ...

  9. Redis与Java - 实践

    Redis与Java - 实践 标签 : Java与NoSQL Transaction Redis事务(transaction)是一组命令的集合,同命令一样也是Redis的最小执行单位, Redis保 ...

随机推荐

  1. Leetcode 523.连续的子数组和

    连续的子数组和 给定一个包含非负数的数组和一个目标整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,总和为 k 的倍数,即总和为 n*k,其中 n 也是一个整数. 示例 1: ...

  2. [oldboy-django][4python面试]有关csrf跨站伪造请求攻击

    1 csrf定义 - csrf定义:Cross Site Request Forgery,跨站请求伪造 举例来说: 网站A伪造了一个图片链接: <a href="http://www. ...

  3. 团队Alpha版本冲刺(二)

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:丹丹 组员7:家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示组内 ...

  4. 事务ACID特性,其中I代表隔离性(Isolation)。

    事务ACID特性,其中I代表隔离性(Isolation). 什么是事务的隔离性? 隔离性是指,多个用户的并发事务访问同一个数据库时,一个用户的事务不应该被其他用户的事务干扰,多个并发事务之间要相互隔离 ...

  5. DocumentFragment批量操作dom

    DocumentFragment,文档片段,不属于文档树,其parentNode为null.当把一个DocumentFragment节点插入文档树时,插入的不是DocumentFragment自身,而 ...

  6. 【bzoj3585】mex 线段树 mex,sg

    Description 有一个长度为n的数组{a1,a2,…,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. Input 第一行n,m. 第二行为n个数. 从第三行开始,每行一个询问l, ...

  7. 解决ie8下页面刚出现时候的晃动问题

    出现这个问题的原因的页面的高度超过一屏,这个时候需要在开始的时候给 html,body {overflow:scroll;overflow-x:hidden}; 这样就可以解决这个问题了

  8. 转:java native

    今日在hibernate源代码中遇到了native关键词,甚是陌生,就查了点资料,对native是什么东西有了那么一点了解,并做一小记. native关键字说明其修饰的方法是一个原生态方法,方法对应的 ...

  9. ai相关

    学习资源 1.1 1.2 2.1 2.2 2.3 前置 octave sklearn python3 git 学习相关 link 定义 Field of study that gives comput ...

  10. HDOJ 1398 Square Coins

    Square Coins Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tota ...