日益丰富的业务场景和不断提高的系统性能要求使得软件系统逐渐走上了分布式的道路. 而电商平台、物联网平台这种需要快速响应市场变化、系统更新迭代频繁的软件系统对系统扩展性、敏捷开发能力提出了更高的要求, 传统面向服务(Service-Oriented Architecture, SOA)架构[1,2]建设的系统由于其粗粒度的服务划分和企业服务总线的设计无法满足这类系统的性能需求, 随着Docker等容器技术[3]和云计算技术的发展, 微服务架构逐渐成为更优的选择.
微服务架构[4–6]是一种架构风格, 将大型复杂系统细粒度的拆分成多个能够独立运行、职能单一的服务, 服务之间通过通用的协议进行通讯. 微服务架构的系统在系统扩展性、敏捷性等方面相比于SOA架构的系统都具有明显的优势, 但是这种细粒度拆分服务的方式势必会使服务治理变得异常复杂. 传统服务治理框架, 如Dubbo、Spring Cloud为微服务服务治理提供了切实有效的解决方案, 但都存在难以实现不同技术框架和通讯协议建设的服务之间互联互通和服务治理与服务高耦合的问题. 本文基于服务网格的思想对微服务服务治理进行改造, 从服务中抽取出服务治理相关功能并集成在网络代理上, 网络代理作为服务治理的独立组件解决了服务治理和服务高耦合的问题, 并且网络代理会对所有进出服务的流量进行拦截, 能够实现不同技术框架和通讯协议建设的服务之间互联互通.
1 相关工作 1.1 服务治理在微服务架构中服务治理是整个系统正常运行的关键技术, 在大型复杂系统环境下, 服务间的调用会变得非常复杂, 如果没有一套完善的、经过大规模生产环境验证的服务治理方案的话, 系统将会处于非常危险的境地. 为解决复杂环境下服务治理的问题, 出现了很多服务治理框架, 其中应用最广泛有Dubbo和Spring Cloud.
Dubbo是由阿里巴巴中间件团队开源的基于Java的高性能远程过程调用[7](Remote Procedure Call, RPC)通讯框架, 在其提供的服务整合能力支持下, 使用RPC可以像使用本地调用一样轻松便捷, 并且Dubbo是阿里巴巴内部SOA服务治理方案的核心框架, 每天为2000多个服务提供数亿次访问量支持[8], Dubbo已不只是单纯的服务通讯框架, 更是一套完备的服务治理框架, Dubbo也因为其优秀的服务治理能力和高效的RPC通讯能力成为了微服务架构的一种优秀解决方案.
Spring Cloud是一系列Spring框架的集合, 以SpringBoot作为开发的基础, 通过SpringBoot可以简单高效地集成服务发现注册、负载均衡、断路器[9]等其他Spring家族的分布式框架[10], 相比于Dubbo框架, Spring Cloud最大的特色在于一站式的分布式系统架构.
但无论是以Dubbo还是Spring Cloud作为服务治理核心框架的微服务系统都需要对所有接入的服务引入组件, 并在业务服务中并暴露或消费相关的服务, 这会大大增加服务治理和业务服务之间的耦合度, 增加服务开发的成本; 此外这种微服务架构系统虽然从一定程度上为不同技术框架和通讯协议建设的服务之间互联互通提供了条件, 但是其实现的成本是相当高昂的, 例如在以Dubbo微服务架构的系统中引入另一个Spring Cloud为技术框架的服务, 那么可能需要对原先系统的每个服务进行升级才能实现, 这在实际场景下很难做到.
1.2 服务网格2017年4月, Buoyant的首席执行官William Morgan在其公司平台首次定义了服务网格[11], 与传统微服务架构不同, 服务网格另辟蹊径, 其实现服务治理的过程不需要改变服务本身, 通过代理或边车形式部署网络代理, 所有进出服务的流量都会被网络代理拦截并加以处理. 这样一来微服务场景下的各种服务治理能力都委托给一个高可用的网络代理, 降低了服务治理和服务的耦合; 而且当具有协议转换功能的网络代理作为服务之间的传输媒介时, 可以很方便的实现基于不同技术框架和通讯协议建设的服务之间互联互通.
当一个系统采用服务网格化的微服务架构时, 系统网络拓扑图如图1所示. 网络代理作为服务治理的独立组件会和每个业务服务部署在同一容器中, 当业务服务需要调用其他服务时, 业务服务只向同一容器下的网络代理发送请求, 实际的服务通讯和治理是在两个网络代理之间完成的, 整个系统以网络代理作为服务间通讯的专用基础设施.
![]() |
图 1 服务网格化的微服务系统网络拓扑图 |
图2展示了微服务和服务网格实现的系统服务调用的区别, 其中图2(a)是传统微服务服务调用方式示意图, 图2(b)是服务网格服务调用方式示意图. 传统微服务没有实现业务服务和服务治理的分离, 服务治理通过框架配置等方式和业务服务部署在同一服务中, 耦合性高, 不利于服务的升级迭代, 开发维护人员除了要完成业务服务之外还要兼顾服务发现注册等服务治理工作, 无疑也增加了开发维护的成本; 此外当系统需要接入不同技术框架和通讯协议建设的服务时, 需要对每个相连的服务增加协议转换功能, 这对于一个大型系统是不能接受的. 服务网格化的服务抽取了服务发现注册等服务治理功能并集成于独立的网络代理, 降低了服务和服务治理的耦合性, 服务只包含具体业务服务, 开发维护人员只需要专注实现业务服务, 提高了开发维护效率; 服务调用时会请求部署在同一容器的网络代理, 实际的服务通讯在两个不同容器的网络代理间完成, 当系统需要接入不同技术框架和通讯协议建设的服务时, 只需要在通用的网络代理中支持协议转换等功能, 不会对服务造成侵入.
![]() |
图 2 服务网格化的微服务和传统微服务服务调用方式 |
综上所述克服传统微服务的两大问题的关键是实现一个独立的、高可用的、具备服务治理、协议转换等功能的网络代理; 另一方面, 由于网络代理的存在, 每次服务调用会增加两次从服务到网络代理的网络开销, 因此网络代理还必须具备高并发、高吞吐量的性能要求. 网络代理功能、性能需求如下:
(1) 良好的服务治理能力. 网络代理作为服务调用的基础设施, 服务治理是其最基础的能力.
(2) 支持协议转换. 目前主流的微服务架构技术框架有以HTTP作为传输协议的Spring Cloud框架、以DUBBO等作为传输协议的Dubbo框架, 技术框架选择很多, 可选的传输协议也很多, 微服务的架构设计为不同技术框架和不同传输协议建设的服务之间互联互通提供了前提条件, 在本文中将协议转换的功能委托给网络代理, 在不同传输协议的服务之间通讯时, 只需要通用的网络代理实现了协议转换功能, 而不需要对每个服务都进行升级.
(3) 支持高吞吐量的网络通讯能力. 系统吞吐量是系统每秒钟处理完的事务数, 是衡量一个系统性能的关键指标. 本文通过代理的方式讲服务治理从服务中独立, 同时也增加了额外的网络开销, 如果网络代理的性能不佳, 会大幅提高系统的响应时间, 因此高吞吐量的网络传输能力也是网络代理必不可少的.
2 网络代理实现针对上文网络代理功能和性能的需求, 本文以图3网络代理的实现机制对网络代理进行构建. 其中consumer-agent和provider-agent是网络代理对应于服务调用的消费者网络代理和生产者网络代理, 图中以Spring Cloud为技术框架的consumer去调用以Dubbo为技术框架的provider为例, 服务调用的流程如下:
(1) 启动 provider-agent后, 会通过Etcd客户端向Etcd注册中心注册服务信息.
(2) consumer通过HTTP协议请求consumer-agent, consumer-agent内部的HTTP服务端接收HTTP请求并交给HTTP编解码器解码, 得到请求信息.
(3) HTTP服务端将请求信息转交给TCP客户端, TCP客户端会根据请求信息获取需要调用的服务, 并通过Etcd客户端向Etcd注册中心拉取提供该服务的provider信息, 如果有多个提供该服务的provider, 会根据加权轮询负载均衡算法选择路由. 确定provider之后, 将请求信息通过protobuf编解码器进行编码, 并通过TCP协议传输给provider-agent的TCP服务端.
(4) TCP服务端通过protobuf编解码器将信息解码, 并交给DUBBO客户端, 再通过DUBBO编解码器将信息以DUBBO协议传输给provider.
(5) Provider将请求解析并进行具体的业务处理, 而后逆反上续步骤将处理结果返回给consumer, 完成一次服务调用.
网络代理的关键技术点有三: 服务治理、网络传输和协议转换, 下文对这三个关键技术点的具体实现和技术选型进行了详细的阐述. 在这之前本文确定选择Java语言实现网络代理, 选择Java语言的原因有两点: 其一Java是分布式系统领域使用最为广泛的语言, 切合网络代理的使用场景; 其二Java语言从诞生开始就主打的几乎完美的跨平台能力, 这对于分布式场景特别是异构系统场景是相当重要的能力.
![]() |
图 3 网络代理实现机制 |
2.1 服务治理
服务治理包括服务注册发现、负载均衡等, 其中服务的注册发现是最重要最基础的服务治理能力, 而负载均衡能力是一个稳定、高性能微服务系统不可缺少的能力, 本文的网络代理设计服务治理包括服务发现注册和负载均衡.
服务注册和发现依赖服务注册中心实现, 在本设计中, 网络代理的轻量化会是更优雅的选择, 因此选用轻量、简单、可靠的注册中心及其客户端会是更优的选择. Etcd是一个分布式的key-value存储系统, 其特点就是简单安全, 方便可靠, 特别由于Etcd提供HTTP+JSON、gRPC接口, 能很好地实现跨语言跨平台的要求, 并且Etcd已经在Google 著名的容器管理工具 Kuberbetes有广泛应用, 是一款完善的、经过大规模生产环境验证的注册中心产品, 与传统的注册中心Zookeeper相比, Etcd在一致性协议、运维管理、社区活跃度等方面都完胜于Zookeeper[12]. 本文采用Etcd作为服务的注册中心, 在网络代理中通过Etcd Java客户端实现服务的发现和注册.
负载均衡算法主要有轮询法、加权轮询法、随机法、原地址哈希法等, 对于配置性能相同的服务来说采用轮询或随机的方法简单有效, 但是多数分布式场景中服务的性能是不相同的, 热点服务或需要大量计算服务的配置往往更好, 这是分布式系统的一大优势. 本文采用加权轮询的方法作为负载均衡的算法, 这种方式能实现对不同配置的服务按需分配不同比例的负载, 提高系统抗压能力, 而且结合Etcd注册中心, 能很好地实现负载调整. 上文说到Etcd是一个key-value存储系统, 在服务注册的时候, 把key作为服务提供者的IP地址, value作为负载权重, 就可以实现对不同服务的加权处理. 在服务消费者网络代理选择路由时, 消费者网络代理将服务发现得到的对应服务生产者网络代理信息保存记录, 并在内部维护一个线程安全的计数器, 每次需要选择负载的时候根据计数器和权重选择路由就可以实现.
2.2 网络传输网络代理需要支持高吞吐量的远程调用能力, 异步非阻塞的网络I/O模型会是更优的选择, 异步非阻塞的网络I/O模型是应用程序接收到I/O操作请求后将I/O处理交给操作系统, 应用程序并不直接参与, 通过事件通知或轮询的方式得到I/O操作的结果, 这种方式的I/O处理模型在高并发或I/O处理耗时长等场景都具有不阻塞网络, 系统资源利用率高的优点. 网络代理采用Netty作为网络I/O模型技术框架, Netty是基于Java的NIO框架, 是Java生态圈首选的网络通讯框架. Netty提供异步的、非阻塞的、事件驱动的网络应用程序框架和工具, 用以快速开发高性能、高可靠性的网络服务器和客户端程序, 由于其良好的线程模型设计, 以Netty实现的服务端可以花费少量系统开销就能实现上万的并发连接[13]. 图3中包括HTTP服务端、TCP客户端、TCP服务端和DUBBO客户端都是基于Netty实现的.
除了网络I/O模型外, 网络传输协议也是网络传输性能的关键因素. TCP面向连接的传输协议相比于UDP无连接传输协议最大的特点在于可靠安全, 这在本文的微服务场景下是非常重要的性能. 在生产环境下, 集群的网络环境常常是不稳定的, 网络传输的可靠性是整个系统的一项重要指标. 网络代理之间的传输协议采用TCP协议, 为了保证传输效率, 不多次反复创建TCP连接, 采用TCP长连接的方式.
网络代理具备高吞吐量的远程调用能力的另一个关键是网络传输时的序列化方式, 不同的序列化方式产生的字节流是不同的, 一般来说, 码流越小的网络传输传输效果更快. 本文采用protobuf作为网络代理之间连接的序列化编解码器, 相比于原生JDK序列化、JackSon、Hessian等序列化方式无论是在处理时间、空间占用方面都有较大优势[14].
2.3 协议转换HTTP和DUBBO协议是目前微服务最常用的通讯协议, 本文的网络代理需要实现这两种协议的互相转换. 上文提到网络传输的设计是基于Netty实现的, Netty提供了一整套完善的NIO客户端、服务端处理框架, 能够很好地处理网络代理之间的连接心跳、协议转换等问题, 网络代理的协议转换功能就是基于Netty的编解码器实现.
3 实验设计 3.1 实验场景实验设计一个以Spring Cloud实现的consumer来消费三个性能不同的以Dubbo实现的provider的场景, 并通过wrk压测工具向consumer施压, 通过lua脚本获取压力测试结果, 实验场景并不复杂(如图4), 评测系统只涉及单接口评测, 将压力测试得到的QPS作为吞吐量的评价分数, 以此来评价网络代理的性能.
得益于Docker容器技术可快速方便地部署本实验的场景. 实验环境部署在两台物理机上, 一台为施压机, 配置为MACOS, 4核8 G, 通过wrk压测工具向另一台物理机施压; 另一台为被压机, 配置为CentOS7, 8核16 G, 通过Docker容器技术部署5个Docker实例:
(1) 以Spring Cloud为技术框架实现的consumer及其网络代理agent, JVM参数为1536 M的堆内存分配.
(2) 以Dubbo为技术框架实现的small-provider及其网络代理agent, JVM参数为512 M的堆内存分配.
(3) 以Dubbo为技术框架实现的medium-provider及其网络代理agent, JVM参数为1536 M的堆内存分配.
![]() |
图 4 实验场景 |
(4) 以Dubbo为技术框架实现的large-provider及其网络代理agent, JVM参数为2560 M的堆内存分配.
(5) 注册中心Etcd.
实验整体流程如下: consumer在接收到客户端请求以后, 会生成一个随机字符串, 该字符串经过consumer-agent和provider-agent后到达provider, 为了模拟现实情况下查询数据库等耗时的操作, provider人为增加50 ms延时, 并计算哈希值后返回, client会校验该哈希值与其生成的数据是否相同, 如果相同则返回正常(200), 否则返回异常(500). 每个通信环节的通讯协议如表1所示.
![]() |
表 1 通讯环节与远程通讯协议 |
3.2 实验结果
实验通过网络代理实现以Dubbo为技术框架、DUBBO为传输协议建设的服务和以Spring Cloud为技术框架、HTTP为传输协议建设的服务之间的通讯, 并且网络代理和业务服务互相独立, 实现了网络代理的功能要求. 另一方面, 实验通过wrk压测工具对实验场景进行施压, 并将QPS作为网络代理性能的评价分数. 为了模拟不同的业务环境, 并发数分别为128, 256和512, 单次实验结果如图5所示, 为减少实验环境的不稳定, 进行10次不同压测记录, 结果如图6所示.
![]() |
图 5 单次压测128、256、512并发实验结果 |
![]() |
图 6 压测10次实验结果 |
由图5和图6可知, 系统平均响应时间去除provider中50 ms的人为延时, 128并发下能做到整个流程的传输时间在4 ms以内, 256并发下整个流程的传输时间在12 ms以内, 并且10次压测结果波动很小, 网络代理在并发量不大的环境下响应速度快, 性能稳定; 在512并发下, 整个流程的传输时间在40 ms以内, 10次压测结果虽然有一定的波动, 但波动浮动不大, 并且将近400万的请求中没有出现异常错误的情况, 网络代理能满足高并发、高吞吐量的性能要求.
实验也对随机法、轮询法和加权轮询法的负载均衡算法进行对比, 通过记录每个provider处理的请求数和压测结果QPS分析不同负载均衡算法的性能, 实验结果如图7所示. 实验表明, 轮询和随机的负载均衡算法三个provider处理的请求数基本相同, 轮询法的QPS相对更加稳定, 并且这两种负载均衡算法在512并发情况下均出现了请求异常的情况, 这是因为没有合理分配负载, 过量的请求将配置低的small-provider线程池撑满溢出, 造成请求失败的情况. 而加权轮询的负载均衡算法三个provider处理的请求数基本按照small:medium:large=1:2:3分配, 负载合理, QPS比另外两种负载均衡算法都高, 并且全程没有出现请求失败的情况. 实验结果表明, 加权轮询的负载均衡算法更适合网络代理的设计.
![]() |
图 7 不同负载均衡算法实验结果 |
为了比较不同序列化方式对网络传输性能的影响, 实验对Java原生序列化、JackSon、Hessian和protobuf进行了对比, 通过10次256并发下的压测结果分析不同序列化方式网络传输的性能, 实验结果如图7所示. 实验结果表明, 基于protobuf序列化方式的系统QPS总是优于其他几种序列化方式, 更适合网络代理的设计.
![]() |
图 8 不同序列化方式的实验结果 |
4 结论
本文基于服务网格思想实现了一个具有服务注册发现、负载均衡、协议转换的网络代理作为微服务架构的服务治理独立组件, 解决了传统微服务架构中服务治理与服务之间高耦合的问题; 并且在每个服务容器中部署网络代理实现了对所有进出服务的流量拦截控制, 通过网络代理能优雅的实现不同技术框架和通讯协议建设的服务之间互联互通. 实验结果表明, 本文的设计实现了以Dubbo技术框架、DUBBO通讯协议建设的服务和以Spring Cloud技术框架、HTTP通讯协议建设的服务之间的互联互通和服务治理从服务中的独立; 另一方面, 实验对系统进行不同并发的压力测试, 实验结果表明本文的设计具备高并发、高吞吐量、高可用的性能要求.
[1] |
郭正敏. 基于SOA架构的分布式服务化治理方案的研究[硕士学位论文]. 南京: 南京邮电大学, 2016.
|
[2] |
Choi J, Nazareth DL, Jain HK. Implementing service-oriented architecture in organizations. Journal of Management Information Systems, 2010, 26(4): 253-286. DOI:10.2753/MIS0742-1222260409 |
[3] |
张忠琳, 黄炳良. 基于OpenStack云平台的Docker应用. 软件, 2014, 35(11): 73-76. DOI:10.3969/j.issn.1003-6970.2014.11.015 |
[4] |
Thönes J. Microservices. IEEE Software, 2015, 32(1): 116. DOI:10.1109/MS.2015.11 |
[5] |
郭栋, 王伟, 曾国荪. 一种基于微服务架构的新型云件PaaS平台. 信息网络安全, 2015, 15(11): 15-20. DOI:10.3969/j.issn.1671-1122.2015.11.003 |
[6] |
王磊. 微服务架构与实践. 北京: 电子工业出版社, 2015.
|
[7] |
Birrell AD, Nelson BJ. Implementing remote procedure calls. ACM Transactions on Computer Systems, 1984, 2(1): 39-59. DOI:10.1145/2080.357392 |
[8] |
Alibaba open sesame. http://dubbo.apache.org/en-us/, 2016.
|
[9] |
Nygard MT. Release it! America: Pragmatic Bookshelf, 2007.
|
[10] |
Spring Cloud. http://projects.spring.io/spring-cloud/, 2017.
|
[11] |
Service Mesh. https://blog.buoyant.io/2017/04/25/, [2017-04-25].
|
[12] |
周佳威. Kubernetes跨集群管理的设计与实现[硕士学位论文]. 杭州: 浙江大学, 2017.
|
[13] |
Maurer N, Wolfthal MA. Netty in Action. Shelter Island, NY, USA: Manning Publications, 2015.
|
[14] |
聂晓旭, 于凤芹, 钦道理. 基于Protobuf的数据传输协议. 计算机系统应用, 2015, 24(8): 112-116. DOI:10.3969/j.issn.1003-3254.2015.08.019 |