转: 低延迟系统的Java实践
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实践的更多相关文章
- 构建数据湖上低延迟数据 Pipeline 的实践
T 摘要 · 云原生与数据湖是当今大数据领域最热的 2 个话题,本文着重从为什么传统数仓 无法满足业务需求? 为何需要建设数据湖?数据湖整体技术架构.Apache Hudi 存储模式与视图.如何解决冷 ...
- 高吞吐、低延迟 Java 应用的 GC 优化实践
本篇原文作者是 LinkedIn 的 Swapnil Ghike,这篇文章讲述了 LinkedIn 的 Feed 产品的 GC 优化过程,虽然文章写作于 April 8, 2014,但其中的很多内容和 ...
- 大型网站系统与Java中间件实践
大型网站系统与Java中间件实践(贯通分布式高并发高数据高访问量网站架构与实现之权威著作,九大一线互联网公司CTO联合推荐) 曾宪杰 著 ISBN 978-7-121-22761-5 2014年4 ...
- java 11 ZGC(可伸缩,低延迟的gc)
ZGC, A Scalable Low-Latency Garbage Collector(Experimental) 可伸缩,低延迟的gc ZGC, 这应该是JDK11最为瞩目的特性, 没有之一. ...
- 高吞吐低延迟Java应用的垃圾回收优化
高吞吐低延迟Java应用的垃圾回收优化 高性能应用构成了现代网络的支柱.LinkedIn有许多内部高吞吐量服务来满足每秒数千次的用户请求.要优化用户体验,低延迟地响应这些请求非常重要. 比如说,用户经 ...
- 系统间通信(5)——IO通信模型和JAVA实践 下篇
7.异步IO 上面两篇文章中,我们分别讲解了阻塞式同步IO.非阻塞式同步IO.多路复用IO 这三种IO模型,以及JAVA对于这三种IO模型的支持.重点说明了IO模型是由操作系统提供支持,且这三种IO模 ...
- 《大型网站系统与JAVA中间件实践》【PDF】下载
<大型网站系统与JAVA中间件实践>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062557 内容简介 到底是本什么书,拥有这样 ...
- Linux低延迟服务器系统调优
最近做了一些系统和网络调优相关的测试,达到了期望的效果,有些感悟.同时,我也发现知乎上对Linux服务器低延迟技术的讨论比较欠缺(满嘴高并发现象):或者对现今cpu + 网卡的低延迟潜力认识不足(动辄 ...
- Redis与Java - 实践
Redis与Java - 实践 标签 : Java与NoSQL Transaction Redis事务(transaction)是一组命令的集合,同命令一样也是Redis的最小执行单位, Redis保 ...
随机推荐
- 实战项目——获取图片中的GPS位置信息和拍摄时间
今天突然看到有人写过获取图片中位置信息的程序.我觉得很有趣,也就自己实践了一下,研究了一下 话不多说,先上代码 #!/usr/bin/env python3 # -*- coding: utf-8 - ...
- JavaScript手册
今天偶然找到javasc的手册地址=>js的手册
- 3种jQuery弹出大图效果
本实例用到了jquery.imgbox.pack.js库.直接看代码: <!DOCTYPE html> <html lang="en"> <head& ...
- 【bzoj2789】[Poi2012]Letters 树状数组求逆序对
题目描述 给出两个长度相同且由大写英文字母组成的字符串A.B,保证A和B中每种字母出现的次数相同. 现在每次可以交换A中相邻两个字符,求最少需要交换多少次可以使得A变成B. 输入 第一行一个正整数n ...
- transform总结
1. 用jquery的css方法获取transform得到的是矩阵matrix,不利于获取translate的值, 优先使用dom.style.webKitTransform进行transform的读 ...
- lcx 端口转发
1.查看3389端口开放情况: REG query HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server\WinStati ...
- swarm 集群管理
1.创建服务 docker service create --replicas 1 --name hello busybox ping baiud.com 2.显示服务详细信息 3.扩展服务数量 4. ...
- [暑假集训--数论]poj1061 青蛙的约会
Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事 ...
- [转].net创建XML文件的两种方法
原文发布时间为:2009-08-26 -- 来源于本人的百度文章 [由搬家工具导入] 方法一:按照XML的结构一步一步的构建XML文档. 通过.Net FrameWork SDK中的命名空间&q ...
- OpenGL ES 画直线代码实例
http://blog.csdn.net/yexiaozi_007/article/details/7978620 以画xyz坐标轴为例,很多人会遇到用glcolor设置了颜色,但是直线画出来还是黑色 ...