简介: 未来,中国工商银行将持续致力于 Dubbo 的金融级规模化应用。

作者:颜高飞,微服务领域架构师,主要从事服务发现、高性能网络通信等研发工作,擅长 ZooKeeper、Dubbo、RPC 协议等技术方向。

Dubbo是一款轻量级的开源Java服务框架,是众多企业在建设分布式服务架构时的首选。中国工商银行自2014年开始探索分布式架构转型工作,基于开源Dubbo自主研发建设了分布式服务平台。Dubbo框架在提供方消费方数量较小的服务规模下,运行稳定、性能良好。

随着银行业务线上化、多样化、智能化的需求越来越旺盛,在可预见的未来,会出现一个提供方为数千个、甚至上万个消费方提供服务的场景。在如此高负载量下,若服务端程序设计不够良好,网络服务在处理数以万计的客户端连接时、可能会出现效率低下甚至完全瘫痪的情况,即为C10K问题。那么,基于dubbo的分布式服务平台能否应对复杂的C10K场景?为此,我们搭建了大规模连接环境、模拟服务调用进行了一系列探索和验证。

C10K场景下Dubbo服务调用出现大量交易失败

准备环境:

使用dubbo2.5.9(默认netty版本为3.2.5.Final)版本编写服务提供方和对应的服务消费方。提供方服务方法中无实际业务逻辑、仅sleep 100ms;消费方侧配置服务超时时间为5s,每个消费方启动后每分钟调用1次服务。

准备1台8C16G服务器以容器化方式部署一个服务提供方,准备数百台8C16G服务器以容器化方式部署7000个服务消费方。

启动dubbo监控中心,以监控服务调用情况。

定制验证场景,观察验证结果:

操作步骤

观察内容

验证结果

场景1

先启动服务提供方,后分批启动消费方

调用1小时观察交易情况

存在零星交易超时失败。消费方分散在多台服务器上。

场景2

在服务正常调用一段时间后,重启提供方

观察提供方重启后的表现

在提供方重启后1-2分钟内存在大量交易超时失败,后逐渐恢复。消费方分散在多台服务器上。

验证情况不尽如人意,C10K场景下dubbo服务调用存在超时失败的情况。

如果分布式服务调用耗时长,从服务消费方到服务提供方全链路节点都会长时间占用线程池资源,增加了额外的性能损耗。而当服务调用并发突增时,很容易造成全链路节点堵塞,从而影响其他服务的调用,并进一步造成整个服务集群性能下降甚至整体不可用,导致发生雪崩。服务调用超时问题不可忽视。因此,针对该C10K场景下dubbo服务调用超时失败情况我们进行了详细分析。

C10K场景问题分析

根据服务调用交易链路,我们首先怀疑交易超时是因为提供方或消费方自身进程卡顿或网络存在延迟导致的。

因此,我们在存在交易失败的提供方、消费方服务器上开启进程gc日志,多次打印进程jstack,并在宿主机进行网络抓包。

观察gc日志、jstack

提供方、消费方进程gc时长、gc间隔、内存使用情况、线程堆栈等无明显异常,暂时排除gc触发stop the world导致超时、或线程设计不当导致阻塞而超时等猜想。

针对以上两种场景下的失败交易,分别观察网络抓包,对应有以下两种不同的现象:

针对场景1:提供方稳定运行过程中交易超时。

跟踪网络抓包及提供方、消费方交易日志。消费方发起服务调用请求发起后,在提供方端迅速抓到消费方请求报文,但提供方从收到请求报文到开始处理交易耗时2s+。

同时,观察交易请求响应的数据流。提供方业务方法处理完毕后到向消费方发送回包之间也耗时2s+,此后消费方端迅速收到交易返回报文。但此时交易总耗时已超过5s、超过服务调用超时时间,导致抛出超时异常。

 

由此,判断导致交易超时的原因不在消费方侧,而在提供方侧。

针对场景2:提供方重启后大量交易超时。

服务调用请求发起后,提供方迅速收到消费方的请求报文,但提供方未正常将交易报文递交给应用层,而是回复了RST报文,该笔交易超时失败。

观察在提供方重启后1-2分钟内出现大量的RST报文。通过部署脚本,在提供方重启后每隔10ms打印established状态的连接数,发现提供方重启后连接数未能迅速恢复到7000,而是经过1-2分钟后连接数才恢复至正常数值。而在此过程中,逐台消费方上查询与提供方的连接状态,均为established,怀疑提供方存在单边连接情况。

我们继续分别分析这两种异常场景。

场景1:提供方实际交易前后均耗时长、导致交易超时

细化收集提供方的运行状态及性能指标:

  1. 在提供方服务器上每隔3s收集服务提供方jstack,观察到netty worker线程每60s左右频繁处理心跳。
  2. 同时打印top -H,观察到占用cpu时间片较多的线程排名前10中包含9个netty worker线程。因提供方服务器为8C,dubbo默认netty worker线程数为9个,即所有9个netty worker线程均较忙碌。

  1. 部署服务器系统性能采集工具nmon,观察到cpu每隔60秒左右产生毛刺;相同时间网络报文数也有毛刺。

  1. 部署ss -ntp连续打印网络接收队列、发送队列中的数据积压情况。观察到在耗时长的交易时间点附近队列堆积较多。

  1. Dubbo服务框架中提供方和消费方发送心跳报文(报文长度为17)的周期为60s,与以上间隔接近。结合网络抓包,耗时长的交易时间点附近心跳包较多。

根据Dubbo框架的心跳机制,当消费方数量较大时,提供方发送心跳报文、需应答的消费方心跳报文将会很密集。因此,怀疑是心跳密集导致netty线程忙碌,从而影响交易请求的处理,继而导致交易耗时增加。

进一步分析netty worker线程的运行机制,记录每个netty worker线程在处理连接请求、处理写队列、处理selectKeys这三个关键环节的处理耗时。观察到每间隔60s左右(与心跳间隔一致)处理读取数据包较多、耗时较大,期间存在交易耗时增加的情况。同一时间观察网络抓包,提供方收到较多的心跳报文。

因此,确认以上怀疑。心跳密集导致netty worker线程忙碌,从而导致交易耗时增长。

场景2:单边连接导致交易超时

分析单边连接产生的原因

TCP建立连接三次握手的过程中,若全连接队列满,将导致单边连接。

全连接队列大小由系统参数net.core.somaxconn及listen(somaxconn,backlog)的backlog取最小值决定。somaxconn是Linux内核的参数,默认值是128;backlog在创建Socket时设置,dubbo2.5.9中默认backlog值是50。因此,生产环境全连接队列是50。通过ss命令(Socket Statistics)也查得全连接队列大小为50。

观察TCP连接队列情况,证实存在全连接队列溢出的现象。

即:全连接队列容量不足导致大量单边连接产生。因在本验证场景下,订阅提供方的消费方数量过多,当提供方重启后,注册中心向消费方推送提供方上线通知,所有消费方几乎同时与提供方重建连接,导致全连接队列溢出。

分析单边连接影响范围

单边连接影响范围多为消费方首笔交易,偶发为首笔开始连续失败2-3笔。

建立为单边的连接下,交易非必然失败。三次握手全连接队列满后,若半连接队列空闲,提供方创建定时器向消费方重传syn+ack,重传默认5次,重传间隔以倍数增长,1s..2s..4s..共31s。在重传次数内,若全连接队列恢复空闲,消费方应答ack、连接建立成功。此时交易成功。

在重传次数内,若全连接队列仍然忙碌,新交易到达超时时间后失败。

到达重传次数后,连接被丢弃。此后消费方发送请求,提供方应答RST。后交易到达超时时间失败。

根据Dubbo的服务调用模型,提供方发送RST后,消费方抛出异常Connection reset by peer,后断开与提供方的连接。而消费方无法收到当前交易的响应报文、导致超时异常。同时,消费方定时器每2s检测与提供方连接,若连接异常,发起重连,连接恢复。此后交易正常。

C10K场景问题分析总结

总结以上造成交易超时的原因有两个:

  1. 心跳机制导致netty worker线程忙碌。在每个心跳任务中,提供方向所有1个心跳周期内未收发过报文的消费方发送心跳;消费方向所有1个心跳周期内未收发过报文的提供方发送心跳。提供方上所连接的消费方较多,导致心跳报文堆积;同时,处理心跳过程消耗较多CPU,影响了业务报文的处理时效。
  1. 全连接队列容量不足。在提供方重启后该队列溢出,导致大量单边连接产生。单边连接下首笔交易大概率超时失败。

下一步思考

针对以上场景1:如何能降低单个netty worker线程处理心跳的时间,加速IO线程的运行效率?初步设想了如下几种方案:

  • 降低单个心跳的处理耗时
  • 增加netty worker线程数,降低单个IO线程的负载
  • 打散心跳,避免密集处理

针对以上场景2:如何规避首笔大量半连接导致的交易失败?设想了如下方案:

  • 增加TCP全连接队列的长度,涉及操作系统、容器、Netty
  • 提高服务端accept连接的速度

交易报文处理效率提升

逐层优化

基于以上设想,我们从系统层面、dubbo框架层面进行了大量的优化,以提升C10K场景下交易处理效率,提升服务调用的性能容量。

优化内容包括以下方面:

具体涉及优化的框架层如下:

经对各优化内容逐项验证,各措施均有不同程度的提升,效果分别如下:

优化内容

优化效果

tcp全连接队列扩容

提供方重启后交易超时失败现象消除

epoll模型调整

提供方重启后全连接队列溢出次数明显降低,连接accept速度有所提升

心跳绕过序列化

提供方在心跳周期无CPU毛刺,CPU峰值降低20%

消费方与提供方之间平均处理时差由27ms降低至3ms

前99%的交易耗时从191ms下降至133ms

增加Iothreads线程数

将默认的iothreads线程数9调整为20后,消费方与提供方之间平均处理时差由27ms降低至14ms

前99%的交易耗时从191ms下降至186ms

提供方心跳打散

从提供方网络抓包分析,心跳数据包的毛刺峰值从1.5万/秒压降至3000/秒

消费方心跳打散

从提供方网络抓包分析,心跳数据包几乎不再有毛刺峰

综合优化验证效果

综合运用以上优化效果最佳。在此1个提供方连接7000个消费方的验证场景下,重启提供方后、长时间运行无交易超时场景。对比优化前后,提供方CPU峰值下降30%,消费方与提供方之间处理时差控制在1ms以内,P99交易耗时从191ms下降至125ms。在提升交易成功率的同时,有效减少了消费方等待时间、降低了服务运行资源占用、提升了系统稳定性。

线上实际运行效果

基于以上验证结果,中国工商银行在分布式服务平台中集成了以上优化内容。截至发文日期,线上已存在应用一个提供方上连接上万个消费方的场景。落地该优化版本后,在提供方版本升级、及长时间运行下均无异常交易超时情况,实际运行效果符合预期。

未来展望

中国工商银行深度参与Dubbo社区建设,在Dubbo金融级规模化运用的过程中遇到了诸多技术挑战,为满足金融级高敏交易的苛刻运行要求,开展了大规模自主研发,并通过对Dubbo框架的扩展和定制持续提升服务体系的稳定性,以“源于开源、回馈开源”的理念将通用增强能力不断贡献至开源社区。未来,我们将持续致力于Dubbo的金融级规模化应用,协同社区继续提升Dubbo的性能容量和高可用水平,加速金融行业数字化创新和转型及基础核心关键的全面自主可控。

原文链接

本文为阿里云原创内容,未经允许不得转载。

工商银行分布式服务C10K场景的解决方案的更多相关文章

  1. 分布式服务追踪与调用链 Zikpin

    分布式服务追踪与调用链系统产生的背景 在为服务中,如果服务与服务之间的依赖关系非常复杂,如果某个服务出现了一些问题,很难追查到原因,特别是服务与服务之间调用的时候. 在微服务系统中,随着业务的发展,系 ...

  2. 高性能的分布式服务框架 Dubbo

    我思故我在,提问启迪思考! 1. 什么是Dubbo? 官网:http://dubbo.io/,DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及作为SOA服务治理的 ...

  3. 基于Greenplum Hadoop分布式平台的大数据解决方案及商业应用案例剖析

    随着云计算.大数据迅速发展,亟需用hadoop解决大数据量高并发访问的瓶颈.谷歌.淘宝.百度.京东等底层都应用hadoop.越来越多的企 业急需引入hadoop技术人才.由于掌握Hadoop技术的开发 ...

  4. 分布式服务框架 Zookeeper — 管理分布式环境中的数据

    本节本来是要介绍ZooKeeper的实现原理,但是ZooKeeper的原理比较复杂,它涉及到了paxos算法.Zab协议.通信协议等相关知识,理解起来比较抽象所以还需要借助一些应用场景,来帮我们理解. ...

  5. 数据迁移的应用场景与解决方案Hamal

    本文来自网易云社区 作者:马进 跑男热播,作为兄弟团忠实粉丝,笔者也是一到周五就如打鸡血乐不思蜀. 看着银幕中一众演员搞怪搞笑的浮夸演技,也时常感慨,这样一部看似简单真情流露的真人秀,必然饱含了许许多 ...

  6. C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案

    C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...

  7. 微言Netty:分布式服务框架

    1. 前言 几年前,我就一直想着要设计一款自己的实时通讯框架,于是出来了TinySocket,她是基于微软的SocketAsyncEventArgs来实现的,由于此类提供的功能很简洁,所以当时自己实现 ...

  8. 个人学习分布式专题(二)分布式服务治理之分布式协调技术Zookeeper

    分布式协调技术Zookeeper 2.1 zookeeper集群安装部署(略) 2.2 zookeeper的基本原理,数据模型 2.3 zookeeper Java api的使用 2.4 zookee ...

  9. 分布式服务(RPC)+分布式消息队列(MQ)面试题精选

    ​ 分布式系统(distributed system)是建立在网络之上的软件系统.正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性.因此,网络和分布式系统之间的区别更多的在于高层软件(特别是 ...

  10. 分布式服务治理框架Dubbo的前世今生及应用实战

    Dubbo的出现背景 Dubbo从开源到现在,已经出现了接近10年时间,在国内各大企业被广泛应用. 它到底有什么魔力值得大家去追捧呢?本篇文章给大家做一个详细的说明. 大规模服务化对于服务治理的要求 ...

随机推荐

  1. linux命令行下使用代理

    有两种方法: 1.curl -x <proxy_ip>:<proxy_port> <real_website> 举例:curl -x 12.99.109.52:80 ...

  2. Android USB开发—USB通信

    USB通信两端分别称为:HOST(USB主机) 与 Device(USB从机/USB配件),常见的主机就是我们的计算机.而Android 可以支持USB主机模式与USB配件模式,意思就是Android ...

  3. cpp面向对象

    面向对象编程 目录 面向对象编程 类 拷贝构造函数 常量函数 友元 友元函数 友元类 静态成员 重载函数 函数重载 操作符重载 继承 多态 虚函数 虚析构函数 1.虚析构函数的作用:避免内存泄漏. 2 ...

  4. Android打造万能自定义阴影控件

    目录介绍 01.阴影效果有哪些实现方式 02.实现阴影效果Api 03.设置阴影需要注意哪些 04.常见Shape实现阴影效果 05.自定义阴影效果控件 06.如何使用该阴影控件 07.在recycl ...

  5. web前端工程化合集

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.Git 1. git 和 svn 的区别 git 和 svn 最大的区别在于 git 是分布式的,而 svn 是集中式的.因此我们不能 ...

  6. 记录--记一次前端CSS升级

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 目前平台前端使用的是原生CSS+BEM命名,在多人协作的模式下,容易出现样式冲突.为了减少这一类的问题,提升研效,我调研了业界上主流的7种 ...

  7. quartus中的时序约束常用方法

    quartus中的时序约束常用方法 一.约束操作 quartus中有三种时序约束方法: 1️⃣Timing Setting 2️⃣Wizards/Timing Wizard 3️⃣Assignment ...

  8. JDBC访问KingbaseES数据库异常 -- 案例分析

    应用使用jdbc访问KingbaseES数据库发生异常:SocketTimeoutException Read timed out 一.异常现象: 应用显示ERROR信息: Caused By: ja ...

  9. archlinux xfce 设置窗口背景颜色,QT背景颜色

    1.使用xfce主题 2.有QT背景不覆盖,使用配置 sudo pacman -S qt5-ct 3.在/etc/environment添加环境变量 QT_QPA_PLATFORMTHEME=qt5c ...

  10. #floyd,分治#D 路径之和

    题目 对于每个\(y\),求除了\(y\)之外,其余的所有点组成的有序点对\((x,z)\) 不经过\(y\)的最短路长度之和(不存在即为-1).\(n\leq 320\) 分析 太妙了,首先用flo ...