基于微服务网关Zuul的TCP功能扩展和限流研究

阅读: 评论:0

2024年1月31日发(作者:)

基于微服务网关Zuul的TCP功能扩展和限流研究

摘 要

随着物联网技术和微服务架构的快速发展,越来越多的基于Mina框架的现场终端设备需要通过TCP通信机制与基于HTTP通信的Spring Cloud微服务系统进行数据交互。而网关Zuul作为外部设备访问微服务系统的唯一入口,并不具备对Mina客户端传送过来的TCP请求进行路由转发的功能,因此本文对服务网关Zuul进行了TCP通信功能扩展。为了保障服务网关Zuul在高并发下的稳定性,结合令牌桶算法实现了限流策略,但该策略无法确保核心服务在高并发情况下的高可用性,并且限流开关无法动态更新,为此本文提出了相应的改进方法并进行了实验分析。主要研究工作内容如下:

(1)针对服务网关Zuul不支持TCP通信机制的问题,本文对网关Zuul进行了TCP功能扩展,其主要工作内容分为三个方面:1)由于网关Zuul并没有与Mina客户端建立连接的功能,本文设计了Mina服务器的通信框架,并通过接口实现的方式构建了Zuul-Mina服务器,成功与Mina客户端建立了通信连接;2)为了解决Mina客户端与微服务实例之间存在通信协议不同的问题,本文设计了Mina客户端与微服务实例之间的通信流程,并通过自定义通信协议及编解码器实现了协议转换;3)对于协议转换后的请求,Zuul-Mina服务器并不具备将其转发到具体的服务实例上的消息代理功能。因此本文通过构建请求转发类,对解析之后的请求进行了重构,并将重构后的请求通过负载均衡转发到了具体的服务实例上。

(2)针对网关Zuul使用令牌桶算法进行限流操作时,存在着无法保障核心服务在高并发情况下的可用性的缺陷,提出了基于URI配置文件的拦截策略,该策略能够对核心URI与非核心URI进行不同的过滤处理,并通过自定义网关过滤器将该拦截策略应用到传统令牌桶算法中,实现了令牌桶算法能够针对不同请求采取不同限流操作的改进,保障了核心服务在高并发下的可用性。该限流策略存在的另一个缺陷在于限流功能的开启与关闭之间的切换,只能通过修改配置文件并重启网关服务来实现,无法做到动态的更新。因此本文在改进的限流策略基础上,通过在自定义的限流过滤器中设置不同的时间段来开启和关闭过滤器,实现了限流开关的动态更新。

(3)对服务网关Zuul的TCP功能扩展进行了功能性测试,通过测试结果分析扩展的TCP通信功能能够正常工作。对改进的限流算法进行功能测试和性I

能测试,并与传统的令牌桶算法进行实验对比,分析了改进的令牌桶算法在核心服务的请求响应时间上具有明显的性能优势。对限流开关的动态更新进行功能测试,验证了其有效性。

关键词:微服务;Zuul;Mina;TCP;令牌桶算法

II

Abstract

With the rapid development of Internet of Tings technology and microservices

architecture, more and more remote terminal devices based on Mina framework need

to upload data to the Spring Cloud microservices system based on the HTTP

protocol through TCP communication. As the only entrance for external devices to

access the microservices architecture, Zuul does not have the function of routing and

forwarding TCP requests sent by Mina clients, so it is necessary to extend the TCP

communication function for the service gateway Zuul. At the same time, in order to

ensure the stability of service gateway Zuul under high concurrency, the token

bucket algorithm is combined to realize the traffic limiting strategy. However, this

strategy could not ensure the high availability of core services under high

concurrency, and the traffic limiting switch could not be updated dynamically.

Therefore, the corresponding improvement studies and experimental analysis are

carried out. The main research works presented in this thesis are as follows:

(1) Since the service gateway Zuul does not directly support TCP

communications, the research and development work of extending the TCP

communication function for the gateway Zuul is carried out. The main work consists

of three parts: 1) For the gateway Zuul lacking of the function of establishing

connections with Mina clients, this thesis designs the Mina server communication

framework, to build a Zuul-Mina server by the interface implementation, which

successfully establishes communications with Mina clients; 2) In order to solve the

problem of different communication protocols between Mina clients and micro

service instances, this thesis designs the communication process between Mina

clients and micro service instances, defines customized communication protocol, and

develops codec for protocol conversion; 3) After the request is converted, Zuul-Mina

server does not have the message proxy function to forward it to the specific micro

service instances. Based on the gateway Zuul, the request forwarding class is

constructed for reconstructing request after parsing. With this class, the reconstructed

request is forwarded to a specific service instance through load balancing.

III

(2) When the gateway Zuul uses the token bucket algorithm for traffic limiting

operations, there is a defect that the core services are unable be guaranteed in high

deal with this problem, a URI configuration file interception strategy

is proposed, which can provide different filtering processing for core URI and

non-core URI. The interception policy is applied to the traditional token bucket

algorithm through a custom gateway filter. So that the token bucket algorithm can be

improved by taking different traffic limiting operations according to the different

requests, and the availability of core services can be guaranteed. Another drawback

of the gateway Zuul when providing the traffic limiting function is that the switching

between the opening and closing of the current limiting operation can only be

achieved by modifying the configuration file and restarting the gateway service, and

cannot be updated dynamically. Therefore, based on the improved traffic limiting

strategy, the dynamic update of the traffic limiting switch is realized by setting

different time periods in the customized filter to turn the filter on and off.

(3) Functional tests are carried out on the TCP function extension of service

gateway Zuul, and the results show that the extended TCP communication function

can work normally. The function and performance of the improved algorithm are

tested. Comparing with the traditional token bucket algorithm, the improved token

bucket algorithm has obvious performance advantages in the core services request

response time. The dynamic update of traffic limiting switch is tested to verify its

effectiveness.

Keywords: Microservices; Zuul; Mina; TCP; Token bucket algorithm

IV

目 录

第1章 绪论 .............................................. 1

1.1 研究背景与意义 ................................................................................................ 1

1.2 研究现状 ............................................................................................................ 2

1.2.1 微服务通信的研究现状 ............................................................................. 2

1.2.2 微服务网关的研究现状 ............................................................................. 3

1.2.3 多通信机制的研究现状 ............................................................................. 4

1.2.4 流量限制策略的研究现状 ......................................................................... 5

1.3 研究内容 ............................................................................................................ 5

1.4 组织结构 ............................................................................................................ 6

第2章 基于服务网关Zuul的相关技术研究 ..............................................

8

2.1 关键技术点分析 ................................................................................................ 8

2.2 网关Zuul技术分析 ........................................................................................... 8

2.2.1 Zuul组件调用关系分析 .............................................................................. 9

2.2.2 Zuul路由配置分析 ...................................................................................... 9

2.3 Apache Mina框架技术分析 ............................................................................. 10

2.4 网关Zuul的限流策略研究 ............................................................................. 12

2.5 整体架构图的提出 .......................................................................................... 14

2.6 本章小结 .......................................................................................................... 15

第3章 基于服务网关Zuul的TCP功能扩展 ...........................................

17

3.1 TCP功能扩展结构图 ....................................................................................... 17

3.2 Zuul-Mina服务器模块 ..................................................................................... 18

3.2.1 Zuul的启动过程分析 ................................................................................ 18

3.2.2 Mina服务器设计原理分析 ....................................................................... 20

3.2.3 Zuul-Mina服务器模块的设计与实现 ...................................................... 21

3.3协议转换模块 ................................................................................................... 22

3.3.1 通信流程的设计 ....................................................................................... 22

3.3.2 自定义报文格式及映射表 ....................................................................... 23

3.3.3 自定义编解码器 ....................................................................................... 25

V

3.4消息代理模块 ................................................................................................... 30

3.4.1 网关Zuul路由转发原理分析 .................................................................. 31

3.4.2 消息代理模块的设计与实现 ................................................................... 34

3.5 本章小结 .......................................................................................................... 36

第4章 基于服务网关Zuul的令牌桶限流算法改进 ..............................

37

4.1 传统令牌桶算法原理分析 .............................................................................. 37

4.2 传统令牌桶算法的改进 .................................................................................. 38

4.2.1 改进方案的提出 ....................................................................................... 38

4.2.2 改进的具体实现 ....................................................................................... 41

4.3 限流开关的动态更新 ...................................................................................... 42

4.4 本章小结 .......................................................................................................... 42

第5章 实验验证和结果分析 ..........................................................................

43

5.1 TCP功能扩展模块测试 ................................................................................... 43

5.1.1 功能验证实验环境搭建 ........................................................................... 43

5.1.2 Zuul-Mina服务器模块功能验证 .............................................................. 44

5.1.3 协议转换模块功能验证 ........................................................................... 46

5.1.4 消息代理模块功能验证 ........................................................................... 49

5.2 令牌桶限流算法改进测试 .............................................................................. 52

5.2.1 令牌桶限流算法改进的功能验证 ........................................................... 52

5.2.2 令牌桶限流算法改进的性能验证 ........................................................... 54

5.2.3 限流开关动态更新功能验证 ................................................................... 56

5.3 本章小结 .......................................................................................................... 57

第6章 总结与展望 .............................................................................................

58

6.1 总结 .................................................................................................................. 58

6.2 展望 .................................................................................................................. 59

致 谢 ......................................................................................................................

60

参考文献 ..................................................................................................................

61

攻读学位期间获得与学位论文相关的科研成果目录 ..............................

64

VI

第1章 绪论

1.1 研究背景与意义

随着计算机应用技术的发展,传统垂直应用架构已经无法满足当今用户的高需求,面向服务的微服务框架得到了快速发展,并被广泛应用于大型企业的系统设计中[1]。同时随着物联网技术的发展,越来越多的远程终端设备需要将现场数据通过网络上传到具体的业务系统,并且有的设备终端还必须和系统服务之间保持长连接,以实现实时监控及各项业务处理[2]。

本课题来源于物联网应用中的虚拟燃气供应商平台,需要将该平台下的燃气表质量分析系统,燃气表维修调度系统、燃气表监控系统等多个系统基于Spring Cloud框架实现微服务化,燃气表具客户端和服务器采用Apache Mina框架开发,需要实时的将燃气表具采集的数据上传到该平台下的各微服务系统中。但是Mina是基于客户端与服务端的TCP/IP协议长连接通信框架[3],而Spring Cloud微服务系统是基于服务网关的RESTful风格的HTTP短连接通信方式[4],两者之间通信机制不同。对于这个问题最直接的解决办法是将微服务系统中各服务扩展成为Mina服务器,与Mina客户端直接进行Socket通信。但这样直接通信会导致客户端多次请求不同的微服务,增加客户端的复杂性,认证复杂,同时随着项目迭代,可能需要重新划分微服务,这样就会导致服务重构难以实施。

Spring Cloud Netflix中的Zuul作为服务网关组件[5],是Spring Cloud微服务架构中一个不可或缺的部分,主要负责将传入的请求路由转发到具体的服务实例。网关Zuul能够屏蔽微服务系统的内部架构实现,统一向外提供REST

API[6],也是Mina客户端访问微服务系统的唯一接入点。但是网关Zuul并不支持多种通信机制之间的交互,无法处理Mina客户端通过TCP通信方式传送过来的请求,因此对服务网关Zuul扩展出一个协议转换以及消息代理的TCP通信功能组件是非常有意义的。

对于服务网关来说,高并发访问情况下的稳定性保障是非常重要的[7]。服务网关Zuul除了具备服务路由、负载均衡、权限控制等功能之外,还具备限流的功能[8]。目前最常用的策略是服务网关Zuul结合令牌桶限流算法实现对流量1

的限制[9],但是这种策略存在的一个问题在于限流的对象是所有到达网关的请求URI,只要当请求量超过了系统设置的阈值,无论当前URI是什么,都会被直接丢弃或重新分发[10]。这种简单的流量限制策略会导致一些核心请求如建立连接、支付转账等相关请求被丢弃,而浏览页面等非核心请求URI被频繁的访问,这样就会影响用户对系统服务的体验感,以及无法保障核心服务在高并发情况下的高可用性。同时限流操作的开启、关闭之间的切换需要通过修改配置和重启服务才能实现,并没有做到限流开关的动态更新。因此实现令牌桶限流算法的改进以及限流开关的动态更新,对服务网关Zuul的性能优化是具有重要意义的。

综上所述,本文旨在对基于Spring Cloud的服务网关Zuul进行TCP通信功能扩展,解决Mina客户端与微服务系统之间的通信问题。同时为了保障服务网关Zuul在高并发请求访问下的稳定性,结合服务网关Zuul对令牌桶算法进行改进以及实现限流开关的动态更新,对传统限流策略进行优化。

1.2 研究现状

1.2.1 微服务通信的研究现状

基于微服务的应用程序是在多个进程或者服务上运行的分布式系统,通常需要跨多个服务器或主机,微服务架构中每一个服务实例通常是一个进程。因此各服务实例之间需要通过进程间通信协议进行交互,根据交互模式的不同,可以把进程间通信协议分为两种:同步的请求响应模式、异步的消息模式[11]。同步模式发送请求之后需要等待响应,而异步模式不需要等待,所以相比于同步模式,异步模式能够大大改善性能以及响应时间。进程间通信协议需要根据不同的场景以及业务功能进行选取,基于微服务的应用程序通常会使用多种通信方式的组合。常用的同步模式实现方式包括基于HTTP的REST和Thrift[12],RSET是面向资源的,其好处在于开发门槛低。Thrift支持多种语言的远程调用,并且可以通过二进制形式传输数据,因此Thrift适用于高并发以及大数据量情况下的通信机制。异步模式的实现可以借用许多成熟的消息中间件,例如RabbitMQ,ActiveMQ,Apache Kafka等[13] [14] [15],它们能够支持多语言接口,同时提供持久、异步以及高性能的通信方式。对于复杂的微服务应用程序,使用异步的消息系统能够实现服务之间的解耦,解决了分布式系统中存在的可靠2

性问题。但是通过消息中间件来实现服务之间的通信,会增加微服务开发的复杂性,同时还需要对消息系统进行额外配置和维护,这增加了系统部署和运维的难度。

文献[16]中提出了通过使用API网关可以提高服务间通信效率的方法。API网关提供了一个反向代理功能,用于将请求重定向或路由到内部微服务的具体方法接口,网关向外部设备提供统一的API访问路径,实现了客户端应用程序与微服务系统的分离。同时,通过API网关将多个客户端请求聚合到一个客户端请求中,然后聚合结果并将响应内容返回到所有客户端应用程序中,通过此设计模式减少了客户端应用程序与后端API网关之间的干扰。

分析上述文献所提出的观点,通过API网关作为访问微服务系统的唯一入口,能够适配多种不同类型终端设备的请求访问,了解到API网关在微服务系统中的重要性,因此本文采用该设计思想,将API网关应用于Mina客户端与微服务系统的通信中,实现请求的路由转发,下一节将主要对微服务网关技术的研究现状进行分析。

1.2.2 微服务网关的研究现状

API网关的流行主要源于近几年微服务架构的兴起与发展[17],原本一个庞大的单体架构的业务系统被拆分成若干个粒度更小的系统进行独立的开发、部署和维护,这种微服务化的架构模式势必会导致不同系统服务之间交互的复杂性,而API网关的出现为微服务架构中的请求转发、认证鉴权、流量控制、协议转换等问题提供了解决方案[18]。因此API网关逐渐成为了微服务架构的标配组件。

目前主流的网关解决方案有很多,例如基于Nginx和Lua语言的OpenResty平台、Mashape提供的一款API管理软件Kong[19]以及Spring Cloud Zuul组件等。现在对几种网关解决方案进行简要说明。

(1)文献[20]对使用API网关构建微服务系统进行了分析和研究,结合OpenResty平台构建了高性能的微服务网关,并提供了流量限制、认证鉴权、路由转发等功能。Nginx是一个基于HTTP的高性能反向代理服务器[21],Lua是一种轻量级且可嵌入式的脚本语言[22],OpenResty是一个用于处理并发的高性能平台,其底层实现是基于Nginx与Lua的整合,通过OpenResty平台开发者可以简单的实现满足自己系统业务需求的API网关。

3

(2)API管理软件Kong本身也是基于Nginx和Lua语言[23],但相比Nginx提供了更简单的配置方式,Kong更便利的地方在于提供了大量的插件来扩展应用,可以通过使用不同的插件为服务提供各种增强的功能。

(3)文献[24]中采用Spring Cloud框架实现了基于微服务架构的高校应用集成方案,并使用网关组件Zuul为该方案构建了服务网关系统。Zuul是Netflix公司开源的网关组件,能够与Netflix的其他组件配合使用,为Spring Cloud框架提供请求路由、身份认证鉴权、动态路由、审查监控等功能[25],而里面有些特性是Nginx所不具备的。

三种常用的网关解决方案都能为微服务系统的开发提供网关基础功能,但是在Spring Cloud微服务框架中,网关组件Zuul能够作为服务实例在注册中心注册,实现高可用,因此相比于其他的网关解决方案,网关Zuul更适用于本文中Mina客户端与Spring Cloud微服务系统之间网关系统的构建,同时由于两者之间通信协议的不同,所以需要对多通信机制的研究现状进行分析。

1.2.3 多通信机制的研究现状

为了解决通信双方存在多通信机制的问题,文献[26]中通过开发通信报文适配器,针对特定的报文格式进行转换适配,使得该适配器能够支持多协议通信。但该方案主要是对通信双方报文格式的适配,没有实现对通信协议的处理,因此在网关Zuul中并不适合引入通信适配器来实现多通信机制。

文献[27]中设计了一个基于嵌入式中间件的协议转换网关,主要原理是通过设计软件和硬件的结构把各种协议进行直接转换来完成通信,实现了工业控制中通信协议间的转换。该方案主要用于实现基于工业总线通信协议的设备之间的通信,并不适用于其他轻量级的通信协议。

文献[28]中设计并实现了基于RocketMQ的MQTT消息推送服务器分布式部署方案,将服务器分为3层:第一层主要负责客户端与服务器之间建立连接;第二层为协议转换层,主要将MQTT传送的消息报文转换为RocketMQ支持的报文格式,同时通过发布订阅的机制将消息推送到RocketMQ的消息队列和MQTT客户端;第三层主要负责将消息发送到总线当中,完成消息的分发与接收。该部署方案采用消息中间件来接收和推送消息,但是引入消息中间件后会导致系统可用性降低,当消息中间件出现网络波动或者宕机时会导致数据的不一致性。

4

文献[29]中通过构建路由模块和通信模块来处理多种通信协议,路由模块主要负责不同协议种类的区分,通信模块主要对路由模块传送过来的通信协议进行相应处理。在微服务系统中,可以参照此设计方案在网关Zuul中构建路由和通信模块来支持多通信机制。

1.2.4 流量限制策略的研究现状

为了确保系统服务在高并发情况下的稳定性,需要一定的限流策略来进行保障。文献[30]利用TCP滑动窗口机制提出了一种基于TCP的流量整型技术[30],该技术主要是通过改变滑动窗口的宽度来对流量发送速率进行有效的调节,从而缓解高并发下的网络压力。但是该方法需要系统对连接进行检测,这样会造成大量的开销,具有一定的局限性。

文献[23]使用网关组件Zuul实现了路由转发的功能,并考虑到在高并发访问情况下网关Zuul的稳定性问题,将网关Zuul作为服务实例,通过不同端口将其注册到服务注册中心中,建立Zuul集群,实现了服务网关Zuul的高可用性,并通过负载均衡请求Zuul集群。但通过建立网关集群的方式会导致系统服务部署架构需要更多的编排和管理能力,影响系统性能。

文献[9]在设计微服务安全访问控制框架中,提出了在请求高并发时,API网关采用令牌桶限流算法进行限流的方式[9],来确保系统的可用性和性能。结合限流算法是目前最常用的限流策略,但是该文献中基于服务网关的限流策略面向的是所有到达网关的请求,并没有考虑到针对URI请求等级的不同,来选择性的进行限流操作。因此本文将在此限流策略的基础上,结合网关Zuul的工作原理对令牌桶限流算法进行改进,实现对限流策略的优化。

1.3 研究内容

本文研究的目标是微服务网关Zuul的TCP通信功能扩展,实现Mina客户端与Spring Cloud微服务系统之间的通信,以及对结合令牌桶算法的限流策略的改进实现。本文的主要研究内容与过程如下:

(1)设计基于微服务网关Zuul的TCP功能扩展的整体结构。通过对网关Zuul的相关技术进行分析,指出了网关Zuul在基于TCP通信机制下的不足之处;为解决该问题,提出在服务网关Zuul的基础上进行TCP功能扩展的需求;5

并通过对Apache Mina框架技术的研究,提出了进行功能扩展需要解决的问题;通过对问题分析,设计了基于服务网关Zuul的TCP功能扩展的整体结构。

(2)分模块对网关Zuul的TCP功能扩展进行了设计实现。1)首先为了实现服务网关Zuul与Mina客户端能够建立连接,对网关Zuul的启动过程和Mina服务器的设计原理进行了分析,设计了Zuul-Mina服务器的通信框架,通过接口实现的方式将网关Zuul与Mina服务器进行了整合,设计并实现Zuul-Mina服务器模块,保障了服务网关Zuul与Mina服务器的同时启动,并成功与Mina客户端建立了连接;2)考虑到通信协议的不一致性,对Mina客户端与微服务系统之间的通信流程进行设计,并自定义通信双方交互的报文格式以及可配置的URL映射表,自定义编解码器实现协议转换模块;3)为了实现对解码之后的消息进行路由转发,对服务网关Zuul的路由转发原理进行分析,构建了请求转发类,并对解析之后的请求进行了重构,设计实现了消息代理模块,将请求转发到具体的微服务实例并获得响应结果。

(3)对服务网关Zuul目前常用的限流策略进行实验和分析,并指出其存在的两大不足:无法保障核心服务在高并发情况下的可用性、限流功能开关无法动态更新,针对两大缺陷提出改进的需求。首先对传统令牌桶算法的原理进行分析,指出其算法流程中的不足之处。通过分析网关Zuul的自定义过滤器原理和限流工具RateLimiter类,提出了结合网关Zuul对传统令牌桶算法进行改进的方案。通过自定义过滤器实现了改进的限流算法;最后在改进的限流策略基础上,通过将当前时间节点与设置的时间段进行对比判断,实现限流开关的动态更新,对改进的限流策略进行优化。

(4)验证TCP功能扩展以及限流策略改进的有效性和可行性。

搭建项目环境,执行测试程序,通过实验结果验证TCP功能扩展各模块的功能有效性;然后对改进的令牌桶限流算法进行功能验证,并结合传统令牌桶算法进行性能对比测试,对测试结果进行分析,验证改进的令牌桶算法在性能上的优势;最后通过功能测试验证了限流开关动态更新的有效性。

1.4 组织结构

本文每一章节的组织结构安排如下:

6

第1章,绪论。首先介绍了本文所选课题的研究背景和意义;然后介绍了目前国内外微服务通信、微服务网关、多通信机制、流量限制策略的研究现状;接着说明了本文的主要研究内容;最后简单介绍了论文的组织结构。

第2章,基于服务网关Zuul的相关技术研究。首先对实现TCP功能扩展和限流策略改进的关键技术点进行了分析;接着对网关Zuul的工作流程以及配置规则进行了分析,指出Zuul在多通信机制下的不足,并提出进行TCP功能扩展的需求;同时对Apache Mina框架的技术进行了分析研究,提出了实现TCP功能扩展过程中需要解决的几大问题;然后对服务网关的限流策略进行研究,指出现阶段的限流策略中存在的两大缺陷;最后通过对提出的问题进行分析,设计了TCP功能扩展和限流策略改进的整体架构图。

第3章,基于服务网关Zuul的TCP功能扩展。首先对TCP功能扩展的整体结构进行设计,介绍了各模块的主要功能;接着分析了网关Zuul的启动过程,Mina服务器的设计原理,通过分析对Zuul-Mina服务器进行了设计与实现;然后设计了Mina客户端与微服务系统之间的通信流程,自定义报文格式及映射表,自定义编解码器实现协议转换模块;最后对网关Zuul的路由转发原理进行分析,以此对消息代理模块进行设计与实现。

第4章,基于服务网关Zuul的令牌桶限流算法改进。首先对传统令牌桶算法原理进行分析,并指出其不足之处;接着对传统令牌桶算法的改进方案进行了设计;并通过改进方案对传统令牌桶算法进行了具体实现;最后通过对限流开关的动态更新进行实现,实现限流策略的优化。

第5章,实验验证和结果分析。首先搭建实验环境和项目环境,设计测试项目,对TCP功能扩展分模块进行功能测试,通过实验结果验证各模块的有效性;对令牌桶算法的改进进行功能测试和性能测试,并分析测试结果来验证改进算法的有效性以及在性能上的优势;最后设计测试用例对限流开关的动态更新进行功能验证。

第6章,总结与展望。首先回顾和总结了本文的主要工作内容,然后对文中仍需要改进的地方以及没有考虑到的地方提出了下一步的研究方向。

7

第2章 基于服务网关Zuul的相关技术研究

本章将对基于网关Zuul的TCP功能扩展和限流策略改进过程中所涉及到的关键技术进行研究。首先对实现TCP功能扩展以及限流策略改进过程中的关键技术点进行了分析;接着对网关Zuul的工作流程和路由配置原理进行分析,指出了网关Zuul在TCP通信机制下的不足之处,提出对网关Zuul进行TCP功能扩展的需求;然后对Apache Mina框架技术进行了研究,并根据研究和分析结果提出TCP功能扩展需要解决的主要问题;最后对网关Zuul的限流策略进行研究,总结出现阶段的限流策略存在的不足之处。

2.1 关键技术点分析

在微服务架构中,网关Zuul作为外部请求的唯一入口,对外屏蔽了系统服务的内部架构实现[31],是微服务系统开发过程中必不可少的一个组件。为了实现现场终端设备与微服务实例之间的通信,首先在2.2节对网关Zuul的相关技术进行分析。

由于Apache Mina框架能够便利地开发高性能和高可用性的网络应用程序,时常用于设备终端与TCP服务器之间的通信[32],因此本文使用Mina框架实现远程终端设备的TCP通信,并在2.3节对其相关技术进行了分析研究。

在实现了系统通信之后,为了保障服务网关Zuul在高并发情况下的稳定性,需要提供有效的限流策略实现对流量的限制[33],因此本文在2.4节对现阶段网关Zuul常用的限流策略进行研究。

2.2 网关Zuul技术分析

本节主要从两个方面对网关Zuul的技术进行分析。首先分析了在Spring

Cloud框架中,网关组件Zuul与其他组件之间的调用关系,并总结了将网关Zuul应用于微服务系统中的优势。接着对网关Zuul的路由配置规则进行分析,了解其工作原理以及现阶段所存在的问题。

8

2.2.1 Zuul组件调用关系分析

Zuul是Netflix开源的微服务网关组件,它可以和Netflix的其它核心组件如Eureka、Ribbon、Hystrix等配合使用[34]。在Spring Cloud框架中,Zuul与其它组件的调用关系如图2-1所示。

图2-1 Spring Cloud组件调用关系图

从上图可以看出,各类请求都得先经过网关组件Zuul才能访问内部服务,通过使用Eureka服务注册发现组件实现高可用的服务注册中心以及实现微服务实例的注册与发现,通过负载均衡工具Ribbon或者服务调用组件Feign实现服务间负载均衡的接口调用,对于依赖的服务调用使用断路器Hystrix来进行包装,实现线程隔离并加入熔断机制,而组件Turbine则担任着服务监控的维护职责。通过各组件的调用关系可以看出,网关Zuul自身包含了对Ribbon和Hystrix等模块的依赖,所以Zuul天生就拥有线程隔离和断路器的自我保护功能,以及对服务调用的客户端负载均衡功能,因此网关Zuul在微服务系统的设计开发中是必不可少的一个组件。

通过上述分析可知,将网关Zuul应用于微服务系统中具有非常明显的优势:1)作为系统的统一入口,屏蔽了内部服务的系统架构;2)可以实现接口权限校验与微服务业务逻辑的解耦;3)可以与服务治理框架结合,实现自动化的服务实例维护以及负载均衡的路由转发;4)减少客户端与各微服务之间的交互次数以及提供认证、鉴权等功能。

2.2.2 Zuul路由配置分析

Zuul的使用方法非常简单,通过创建Spring Boot应用程序,并在其应用主类中使用@EnableZuulProxy注解开启Zuul的API网关服务功能,然后在应用9

程序的配置文件中配置Zuul网关服务的相关基础信息,包括服务名和端口号等,最后通过在配置文件中配置路由规则就可以实现服务网关Zuul的路由转发功能[35]。网关Zuul的路由配置规则包括传统路由配置和服务路由配置两种[36],接下来对两种路由规则进行简要分析。

(1)传统路由配置

传统路由配置方式是在不依赖于服务发现机制的情况下,通过在配置文件中具体指定每个路由表达式与服务实例的映射关系来实现API网关对外部请求的路由,通过..path与..url参数对的方式进行配置,其中path指定了访问服务实例中REST风格的接口路径,url指定了访问服务实例的具体的IP和端口地址。

(2)服务路由配置

对于服务路由,网关Zuul的配置中不需要指定具体实例的IP以及端口地址,而是通过Eureka提供的服务发现注册的机制,将网关服务以及其他微服务实例注册到服务注册中心中,并通过服务注册中心来维护各服务实例,向网关Zuul提供服务实例清单,建立服务名与具体的实例地址之间的映射关系。因此只需要在..path与..serviceId的参数对中指定访问的REST风格接口路径以及访问实例的服务名,就可以实现面向服务的路由转发功能。

不论是传统路由配置还是基于服务的路由配置,从配置规则我们可以看出,网关Zuul的路由路径都是基于HTTP的REST风格URL,所以对于其他的通信机制,网关Zuul是不支持请求的路由和转发的。因此为了实现Mina客户端与微服务系统之间的通信,需要对服务网关Zuul进行TCP功能扩展,而具体的如何实现还需要对Mina框架进行分析研究。

2.3 Apache Mina框架技术分析

Apache Mina主要是对TCP/IP协议的通信框架,提供了事件驱动和异步操作的编程模型,Mina默认使用Java NIO库作为底层支持,上层基于事件驱动提供了异步接口[37],其工作流程如图2-2所示。

10

I/O service:IoConnector线程池IoProcessorI/O service:IoAcceptor线程池IoProcessorIoFilter1IoFilter2IoFilter3receivesendIoHandlerIoFilter1IoFilter2IoFilter3receivesendIoHandler图2-2 Apache Mina工作流程图

(1)I/O service:负责具体的I/O相关工作,处理请求并将数据封装到缓冲区中。主要包括IoAcceptor、IoConnector、IoSession、IoProcessor等接口。

(2)IoConnector:基于Mina框架的网络通信客户端,是TCPClient接口,主要增加了ConnectFuture connect()方法,用于与Server端口建立连接,这个方法是异步执行,可以同时连接多个服务端。

(3)IoAcceptor:基于Mina框架的网络通信中的服务端,是TCPServer的接口,主要增加了bind()方法监听端口,unbind()方法解除对套接字的监听,IoAcceptor可以多次调用bind()方法,同时监听多个端口。

(4)IoProcessor:该接口主要负责在线程通信中,对数据进行读写操作,同时执行过滤器链以及自定义的业务逻辑。

(5)IoFilterChain:负责定义过滤器,开发者通过往Chain中添加IoFilter,来增强处理流程,将业务代码和数据包处理代码分离,提供日志输出、黑名单过滤、编解码处理等功能,我们在使用Mina的时候最关键的在于数据中的encode和decode。

(6)IoHandler:通过开发者实现接口,负责处理业务逻辑。

Apache Mina使用事件驱动的机制处理服务端的ACCEPT事件和I/O的读写。Mina服务端通过创建IoAcceptor对象用于管理与客户端的连接,每当有新的连接请求,IoAcceptor会将连接封装成IoSession并交给线程池中的IoProcessor实例执行该通道上的过滤器,实现对原始数据进行编解码等工作。

11

通过Mina框架进行数据传输时,编解码器的设计是通信过程的核心模块,负责分段消息的组合还原。消息报文首先通过编码器以二进制字节流的形式在网络通道中传输,然后通过解码器将字节流数据解析为具体的JAVA对象,用于具体的业务逻辑处理中。所以需要我们自定义实现编解码器,在编码时将JAVA对象转换为二进制字节流,解码的时候将字节流向JAVA对象进行转换。

Mina框架的通信机制主要是基于传输层的TCP协议实现的Socket通信[38],而微服务系统中的HTTP协议是基于应用层的文本协议,两者之间进行交互的报文格式不同,因此为了实现两者之间的通信,就必须通过自定义通信双方的报文格式实现TCP/IP协议与HTTP协议之间的转换。

通过对Apache Mina框架技术进行分析,发现其通信机制是基于Mina客户端与Mina服务端之间的Socket通信,而Zuul作为基于Spring Cloud框架的网关服务系统,并没有处理Mina服务端通信流程的功能,因此对服务网关Zuul进行TCP功能扩展的首要任务是将网关Zuul与Mina服务器进行整合,使其能够与Mina客户端建立连接并接收请求。同时为了匹配网关Zuul的路由规则,需要自定义通信双方的报文格式,Mina框架本身所提供的常用的编解码器无法满足本文研究的需要,所以需要自定义编解码器来实现协议转换。在网关解析到Mina客户端发送过来的请求报文之后,如何实现消息代理将请求路由转发到具体的服务实例上也是实现网关Zuul的TCP功能扩展的关键点。

2.4 网关Zuul的限流策略研究

实现了微服务系统与外部客户端的通信后,应当考虑网关Zuul在高并发情况下的稳定性,采用一定的限流策略来对并发的请求进行限速,一旦达到限制速率限流策略能够对请求进行拒绝、等待或者降级等处理。网关Zuul能够结合限流算法来实现对流量的限制,目前常用的限流算法主要包括漏桶算法和令牌桶算法。

漏桶算法的运行机制就像一个底部带有一个漏洞的漏桶,流量从桶口流入桶中并从漏洞流出,不管进入桶内的流量有多少,从底部的漏洞流出流量的速率是一定的[39]。漏桶算法通过漏桶队列控制器来控制流量的流入和流出,队列的头部就是桶的漏洞,从队头流出的数据流量的速率是固定,当请求流量超过队列的容量时多余的将会被丢弃,采用漏桶算法可以保障传送到网络中的流量速率不会比网络要求的速率高,但是对于很多应用场景来说,除了需要对数据12

传输的平均速率进行限制,还需要允许高并发情况下的突发传输,这时候漏桶算法就不是最合适的限流算法了。

令牌桶算法的原理是采用令牌桶队列控制器来管理流量的流入和流出[40],系统会以恒定的速率往桶里放入令牌,如果请求需要被处理时,则需要从令牌桶中获取到足够数量的令牌,如果当前桶内没有足够的令牌供请求获取,则会拒绝该请求。通过此方法令牌桶算法不仅可以控制网络传输速率,并且在高并发情况下还能允许突发数据的发送,其中突发数据的大小最多为令牌桶的容量大小。因此,相比于漏桶算法,令牌桶算法更适合于应用到服务网关Zuul中,用于处理到达网关的突发流量以实现限流操作。

网关Zuul可以通过使用Google提供的限流工具类RateLimiter来实现令牌桶算法的流量控制。使用过程如下:

(1)导入Zuul-RateLimiter依赖,依赖如下。

spring-cloud-zuul-ratelimit

E

(2)添加配置信息。在配置信心中配置是否开启限流操作,刷新时间窗口的时间,以及该时间窗口内对应的请求数量限制,限流方式等。配置信息如下。

zuul:

ratelimit:

key-prefix: your-prefix #对应用来标识请求的key的前缀

enabled: true

repository: REDIS #对应存储类型(用来存储统计信息)

behind-proxy: true #代理之后

default-policy: #可选 - 针对所有的路由配置的策略,除非特别配置了policies limit: 10 #可选 - 每个刷新时间窗口对应的请求数量限制

refresh-interval: 60 # 刷新时间窗口的时间,默认值 (秒)

type: #可选 限流方式

- user

- origin

- url

13

通过该配置,网关Zuul就能实现在一分钟内最多允许10次请求进行访问,如果超过了10次,则会拒绝超过限制的请求,使用jmeter测试工具进行并发访问,其请求结果如下图所示。

图2-3 网关Zuul限流测试图

从测试结果可以看出,在一分钟内如果请求次数超过了10次,多余的请求就会直接被拒绝,无论当前请求是否是核心请求,网关Zuul结合令牌桶算法的限流策略都会将其拒绝。因此该策略存在的一个缺陷在于限流的对象面向的是所有达到网关的请求,并没有对请求进行过滤处理,这种限流策略过于简单粗暴,会导致在高并发情况下核心的流程被丢弃,非核心的请求被频繁访问,无法保障核心服务的可用性,因此需要对到达网关的请求进行过滤处理。该策略存在的另外一个缺陷在于当通过配置文件开启限流操作后,并不能动态地对限流操作的开启和关闭进行切换,只能通过修改配置文件并重新启动网关服务,才能关闭或者开启限流操作,因此需要实现限流开关的动态更新。

2.5 整体架构图的提出

通过对服务网关Zuul和Apache Mina框架的相关技术进行研究,并对网关Zuul的限流策略进行了实验分析,对实现TCP功能扩展和限流策略改进过程中存在的主要问题分析如下:

网关Zuul的路由配置都是基于HTTP协议的REST风格请求,并不支持对TCP协议下的请求进行路由转发,因此对服务网关Zuul进行TCP功能扩展是有必要的。

14

Apache Mina通信框架是基于Mina客户端与Mina服务端的Socket通信机制,为了实现Mina客户端与微服务系统之间的通信,需要实现服务网关Zuul与Mina服务器的整合,与Mina客户端建立连接,同时需要自定义通信协议和编解码器,实现两者协议的转换,最后为了将解析出来的请求转发到具体的微服务实例,需要在网关Zuul中实现消息代理的功能。

通过研究分析网关Zuul结合传统令牌桶算法进行限流操作的过程,发现该限流策略存在两点不足之处:1)由于令牌桶算法限流的对象是所有到达网关的请求,没有对核心请求与非核心请求进行过滤处理,导致了在高并发情况下,无法保障核心服务的可用性。2)限流操作的开启与关闭只能通过修改配置文件并重启网关服务才能进行更新,没有做到限流开关的动态更新。因此针对这两点缺陷,对服务网关Zuul结合令牌桶算法的限流策略进行改进是很有必要的。

结合以上所提出的问题,通过将各功能的实现模块化,在网关组件Zuul的基础上,构建TCP功能扩展和限流策略改进的整体架构图,该架构图如图2-4所示。

TCP功能扩展Zuul-Mina服务器模块协议转换模块限流策略改进令牌桶限流算法改进限流开关动态更新消息代理模块网关组件Zuul图2-4 整体架构图

从架构图可以看出,TCP功能扩展主要包括了三个模块:Zuul-Mina服务器模块、协议转换模块、消息代理模块;对网关Zuul的限流策略改进主要分为对令牌桶限流算法的改进以及对限流开关动态更新的实现。其中各模块的具体实现,本文在第3章、第4章进行了详细的研究和分析。

2.6 本章小结

本章首先在2.1节对实现TCP功能扩展和限流策略改进的关键技术点进行了简要分析;然后在2.2节对网关Zuul的技术进行了详细分析,指出了网关Zuul15

在多通信机制下存在的不足之处,提出了对Zuul进行TCP功能扩展的需求;接着在2.3节对Apache Mina框架的相关技术进行了研究,提出了实现TCP功能扩展需要解决的主要问题;其次在2.4节对网关Zuul的限流策略进行了研究,通过实验提出了结合令牌桶算法的限流策略存在的两大缺陷;最后在2.5节通过对存在的问题进行分析,提出了基于服务网关Zuul的TCP功能扩展和限流策略改进的整体架构图。

16

第3章 基于服务网关Zuul的TCP功能扩展

通过对第2章相关技术的研究与分析,提出了基于模块化的架构方案以解决网关Zuul的TCP功能扩展问题,其中TCP功能扩展包括的主要模块有:Zuul-Mina服务器模块、协议转换模块、消息代理模块。本章将对这三个模块进行具体的设计与实现。

3.1 TCP功能扩展结构图

服务网关Zuul的TCP功能扩展整体结构图如图3-1所示。

Micro-Service

A网关组件ZuulZuul-Mina服务器模块Micro-Service

A1TCPMina客户端消息代HTTP理模块Micro-Service

B协议转换模块Micro-Service

C图3-1 TCP功能扩展整体结构图

TCP功能扩展整体包括了对Zuul-Mina服务器模块、协议转换模块、消息代理模块三个模块的设计与实现。Zuul-Mina服务器模块主要是对服务网关Zuul进行改造,使其能够作为Mina服务器与Mina客户端建立连接进行通信,协议转换模块主要负责自定义的报文格式与二进制字节流之间的编码解码操作,消息代理模块负责将协议转换之后的请求路由到微服务系统中的实例并返回结果,同时调用负载均衡算法来选择实例,实现Mina客户端与Spring Cloud微服务系统之间的通信。

17

3.2 Zuul-Mina服务器模块

为了能在启动网关服务时,同时启动Mina服务器,所以要将Zuul网关服务与Mina服务器进行整合,首先对服务网关Zuul的启动过程和Mina服务端的通信过程进行分析,并设计方案实现功能。

3.2.1 Zuul的启动过程分析

由于Spring Cloud的构建是基于Spring Boot实现的[41],所以服务网关Zuul也是一个Spring Boot应用程序,其启动结构图如图3-2所示。

SpringApplication开始启动New

SpringApplication()构造一个Spring应用()启动此应用初始化模块应用启动计时器开始计时应用启动监听器开始监听SpringApplicationRunListners应用启动监听模块ConfigurableEnvironment配置环境模块ApplicationContext应用上下文模块应用启动计时器结束计时应用启动监听器结束监听()启动结束图3-2 Spring Boot启动结构图

从图3-2可以看出,整个启动流程主要分为三个部分,第一部分进行SpringApplication的初始化,配置一些基本的环境变量、资源、构造器、监听器;第二部分实现了具体的启动方案,包括启动流程的监听模块、加载配置环境模块以及创建上下文环境模块;第三部分是自动化配置模块。每一个Spring

Boot应用程序都有一个主入口,也就是main方法,main方法里面通过调用()启动整个Spring Boot程序,run()方法的执行过程如图3-3所示。

18

()发布ApplicationReadyEvent事件发布ApplicationStartedEvent事件更新上下文refreshContext装配环境,发布ApplicationEnvironmentPreparedEvent事件启动所有监听ApplicationStartedEvent事件的监听器打印Banner,创建ApplicationContext装配Context,发布ApplicationPreparedEvent事件图3-3 run()方法执行过程图

(1)run()方法执行的第一步是初始化Spring Boot自带的监听器,以及添加到SpringApplication的自定义监听。

(2)Spring Boot通过发布一个ApplicationStartedEvent事件,但并没有启动监听器。

(3)初始化参数,装配环境,确定是web环境还是非web环境,装配完后触发ApplicationEnvironmentPreparedEvent事件,启动监听该事件的监听器。

(4)打印启动Banner,根据web环境创建ApplicationContext,将获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。

(5)装配Context,设置Context的环境变量和注册,发布事件ApplicationPreparedEvent,目前源码是空的,暂时无法监听此事件。

(6)发布一个ApplicationStartedEvent事件来启动所有监听ApplicationStartedEvent事件的监听器。

(7)更新应用上下文,refreshContext负责装配Context BeanFactory等重要组件,afterRefreshContext负责查找当前ApplicationContext中是否注册有自定义的CommandLineRunner,如果有,遍历执行他们的run()方法。

(8)发布ApplicationReadyEvent事件,启动完毕,服务可以正常提供服务,如果启动失败,会发布ApplicationFailedEvent事件并且触发监听器。

通过分析()的整个启动过程,发现Spring Boot给我们提供了一个CommandLineRunner接口,该接口用于启动应用时做特殊处理,通过实现CommandLineRunner接口,并重写其中的run()方法,在19

afterRefreshContext阶段会遍历执行所有实现该接口的run()方法,这样就可以在SpringApplication的run()方法执行完成之前处理一些特殊的代码模块,比如特殊数据的加载、微服务的服务发现注册、系统启动成功之后的通知等。因此可以通过实现CommandLineRunner接口的run()方法来实现Mina服务器的创建。

3.2.2 Mina服务器设计原理分析

要创建一个基于Mina框架的应用程序,客户端和服务器都必须创建I/O

Service负责处理I/O,执行IO操作,还需要创建过滤链,负责编解码等处理,最后还需创建Handler,负责处理业务逻辑。Mina服务端的框架图如图3-4所示。

NetWorkI/O Service: AcceptorProcessorIO Filter1IO Filter2IO Filter3I/O Handler图3-4 Mina服务端框架图

从上图可以看出服务端的主要逻辑思路如下:

(1)服务端通过SocketAcceptor创建Acceptor对象,负责监听客户端连接。

(2)为每个新的连接创建一个session,同一个端口和IP的后续请求将通过session处理。建立连接之后通过Processor多线程处理数据的读写。

(3)同一个session收到的数据通过过滤链进行过滤操作,通过编解码器进行有效地编码、解码处理,完成消息的过滤和格式的转换。

(4)最后过滤器链将数据交给Handler进行相应的业务处理。

上述逻辑思路主要完成了Mina服务端读取数据的过程,服务端写入数据的过程与读取数据的过程正好相反。首先在Handler的方法中对需要传送的数据进行写入处理,通过调用()方法将数据写出,然后将数据交由过滤

器链进行协议转换,最后通过Processor多线程将编码后的二进制字节流数据写出到网络中。

20

通过对Mina服务端的设计原理进行分析,本文设计了网关Zuul中Mina服务器的通信框架,如图3-5所示。

Mina客户端ZuulIoServiceIoProcessorIoFilterChainLogging filterProtocol Coder FilterDecoderEncoderIoSessionIoHandler消息代理模块图3-5 自定义Zuul-Mina服务器通信框架图

从上图可以看出,Zuul-Mina服务器的框架设计主要由IoService、IoProcessor、IoFilterChain、IoHandler、IoSession以及消息代理模块组成,其中IoService主要负责会话管理,通过创建Acceptor对象用于监听客户端连接请求;IoProcessor通过多线程的方式对网络传输中的数据进行读写操作;IoFilterChain过滤器链对Processor读取的数据进行过滤操作,主要负责数据的编解码以及协议的转换,也可以通过自定义过滤器实现对数据的特殊处理;IoHandler对数据进行业务处理,将转换过后的请求转发到消息代理模块;消息代理模块负责将处理后的请求转发到具体的微服务实例,并获得响应结果返回给客户端;IoSession负责保持Mina客户端与Zuul-Mina服务器之间的会话连接。

3.2.3 Zuul-Mina服务器模块的设计与实现

为了实现服务网关Zuul与Mina服务器的同时启动,通过创建类MinaRun并实现CommandLineRunner接口,让该类在服务启动完成之前执行。创建类MinaServerHandler实现IoHandler接口,负责处理Mina服务器的业务逻辑,实现Zuul-Mina服务器的类关系图如图3-6所示。

21

图3-6 Zuul服务Mina化实现类关系图

MinaServerHandler类实现了IoHandler接口提供的方法,具体的业务逻辑处理在消息代理模块会进行详细的描述。MinaRun类重写CommandLineRunner接口的run()方法的主要流程如下:

(1)通过NioSocketAcceptor类创建Acceptor对象。

(2)Acceptor对象通过调用t()方法添加自定义的日志过滤器LoggingFilter(),并向编解码过滤器链中添加自定义的编解码工厂类ServerProtocolCodecFactory()。

(3)通过setHandler方法设置自定义的业务逻辑类MinaServerHandler。

(4)Acceptor对象调用bind()方法,指定监听端口,当有Mina客户端连接请求时,建立会话连接,完成对Zuul-Mina服务器的设计实现。

3.3协议转换模块

3.3.1 通信流程的设计

从本文2.1节对网关Zuul的路由配置的分析可知,网关Zuul只能处理基于HTTP的REST风格请求,要实现Mina客户端与各微服务之间的通信,就必须自定义传输的协议格式,同时协议内容中要包含客户端需要访问的URL,然后通过解码操作获取访问的URL,最后通过路由负载模块进行转发。但是这样会增加协议格式的复杂性,同时由于URL长度很大,而解码器每次读取缓冲区的22

数据是有限制的,可能会导致数据缺失等问题,所以需要将URL存放在服务网关的配置文件中,通过长度为4位的字符串映射号与每条URL进行一对一的映射,通过查找映射表得到相应URL。该过程如图3-7所示。

开始启动客户端,发送自定义报文解析协议,查找映射表中是否对应?是匹配URL,消息代理模块进行转发否结束图3-7 客户端与内部服务通信流程图

规定Mina客户端与微服务实例之间的通信过程如下:

(1)客户端使用Mina NioSocketConnector与服务网关Zuul建立Socket连接,发送自定义的报文。

(2)服务网关Zuul通过解码器解码报文,获取URL映射号,根据映射号查找映射表中的URL,对URL重构加上所传参数。如果查找不到返回“error”信息到客户端。

(3)获得请求URL之后通过后面的消息代理模块将其转发到内部服务上并返回响应结果。

3.3.2 自定义报文格式及映射表

协议转换模块的设计首先需要考虑的是通信双方交互的报文格式,通过自定义报文格式可以实现通信双发消息传递格式的统一,同时Mina客户端与服务端能够使用同样的编解码器实现协议转换。目前,常用的报文定制方法有[42]:

(1)定长消息法:该方式规定了传送数据的长度,一般用于传送命令帧来发送控制指令。

23

(2)定长报文头法:消息报文分为报文头和报文体两部分,报文头长度固定,同时在报文头中指明消息报文的整体长度,真实数据内容放在报文体中。读取消息报文时通过报文头中指明的数据长度来判断报文体中的真实数据内容是否读取完整。该方法是目前使用最为广泛的一种报文定制方法。

(3)字符定界法:通过在传送的数据中添加特殊字符作为结束符,一般使用ASCII码中存在的特殊字符,读取数据时判断当前读取数据是否为特殊字符,如果不是则继续读取,如果是特殊字符则结束读取。该方法一般用于通信双方简单数据的发送。

本文为了满足功能需求,选用字符定界法实现统一的报文格式,通过使用换行符作为分隔符,客户端与服务器之间的通信格式如下:

报文源(2位) 传输类型(2位)URL映射号(4位)数据

其中报文源固定为两位整数的字符串用于标识报文的来源,01代表该消息来自Mina客户端,10代表消息来自于微服务。传输类型也是固定2位大小的字符串用于标识传输的消息类型,其中01代表请求报文,10代表响应报文。URL映射号为固定4位长度的整数字符串,用于匹配映射表中的URL。数据部分的字符串大小不固定。

当URL映射号查找不到匹配的URL路径或者该路径下没有可用的实例时,则网关Zuul需要向客户端返回错误信息“error”,该字符串经过编码之后的报文格式为一个长度为5的字节数组,数组中的每一个值代表字符串“error”中每个字符所对应的ASCII码值,具体的报文格式如下:

101 114 114 111 114

Mina客户端根据网关Zuul传回来的信息在消息处理模块做出不同的响应,

如果返回来的是具体的响应内容,客户端输出该内容,如果返回来的是“error”,则客户端提示当前请求没有可用URL路径或者当前请求没有可用服务实例。

通过定义一个实体类Token来保存协议内容,核心代码如下:

public class Token {

private String source; //报文源

private String type; //传输类型

private String urlcode; //URL映射号

private String data; //数据

/***getters & setters***/ //getters & setters方法}

24

为了使开发者能够更方便的使用本文所设计的服务网关扩展功能,本文将URL映射号和所对应的URL设计成可配置的形式,能够对这些参数进行统一化的管理和维护更新,使开发者在编码过程中能够避免与具体业务无关的参数。URL映射表采用xml文件的形式,名称为,具体的配置内容如下所示。

<?xml version="1.0" encoding = "UTF-8" ?>

0001

localhost:3433/eurekaclient1/test/test2

其中urlcode代表URL映射号,urlpath表示该映射号下的URL访问路径。

3.3.3 自定义编解码器

编码的过程在于将一个消息对象转变为二进制字节流用于网络通信,解码的过程就是将从网络通道中接收的二进制字节转换为消息对象。在Mina框架中,不同的通信协议需要不同的编码器和解码器,为此Mina提供了一种“多路分离”的思想,通过自定义编解码工厂类,并将其加入到编解码过滤器链中,程序启动之后,会将工厂类中自定义的编解码器注册到一个集合中,在需要进行解码操作时遍历该集合,并查找合适的解码器。

通过对Mina框架源码的分析,发现Mina框架自带了三种解码器[43],如表3-1所示。

表3-1 Mina解码器表

解码器

CumulativeProtocolDecoder

TextLineDecoder

SynchronizedProtocolDecoder

累积性解码器

按照文本换行符解码

能够保障线程安全的解码器,但在高并发情况下会影响性能

说明

其中TextLineDecoder类解码器是按照字符定界法来对二进制流进行解析,通过设定特殊的字符,在解析过程中判断解析的数据是否等于该特殊字符的25

ASCII码值,如果相等则结束接收数据并进行解析。由于解码器每次读取缓冲区的数据大小是有限制的,即ReadBufferSize的大小,默认是2048个字节,使用TextLineDecoder类解码器会对数据分多次读取,从而造成断包、粘包问题。

粘包是指将网络中多次间隔较小且数据量较小的数据合并成了一个大的数据块,断包是指由于消息长度过大,解析过程中可能会将其分片,当出现断包或粘包现象时,接收端无法确定收到的消息是不是一个完整的消息,所以TextLineDecoder类解码器适用于数据大小较为固定且数据量较小的协议转换。当出现断包粘包现象时,可以通过解码器类CumulativeProtocolDecoder来解决相关问题。

CumulativeProtocolDecoder类称为累积性解码器,相比于TextLineDecoder中setReadBufferSize(newsize)的方式,CumulativeProtocolDecoder类累积数据的方式不需要设置缓冲区大小,当监测到有网络中的数据到达,该类就会去读取数据,将读取到的数据累积到缓冲区中,然后通过子类中自定义的解码方法doDecode()把缓冲区累积的数据解码为消息对象。累积数据的方式能够解决断包粘包现象,同时还能节省缓冲区空间并提高数据处理的效率。该类累积数据流程如图3-8所示。

图3-8 CumulativeProtocolDecoder类累积数据流程图

从流程图可以看出,Mina服务端的Processor线程监测是否有数据到达,当有数据到达时通过自定义的解码方法doDecode()进行解码,并判断方法返回否是否开始是Processor线程检查I/O状态是否有数据?是doDecode()方法解码返回值是否为True?是是否读取了缓冲区数据?是缓冲区是否还有未读数据?否缓冲区是否还有未读数据?否清空缓冲区数据否抛出IO异常结束26

值,如果返回值为true,则CumulativeProtocolDecoder类中的decode()方法会判断解码过程是否读取到了缓冲区中的数据,如果没有就直接抛出I/O异常,如果读取到了decode()方法会检测是否还有数据在缓冲区中,如果存在就继续执行doDecode()方法进行解码操作,如果没有就停止对doDecode()方法的调用。

如果deDecode()方法返回false时,CumulativeProtocolDecoder会停止对doDecode()的调用,但此时如果缓冲区的数据还有未读取完的,就将缓冲区中未被读取的数据保存到会话中,以便下次数据到来时可以从会话中提取合并,防止出现断包现象,如果发现读取完毕,则清空缓冲区。

第三类解码器SynchronizedProtocolDecoder能够保障在解码过程中的线程安全。由于Mina框架采用的是多线程来处理对数据的读写,所以在对数据解码的过程中,有可能出现执行解码方法的线程不是上一次线程的问题,这样会导致线程不安全,因此可以通过SynchronizedProtocolDecoder类进行同步操作来保证线程的安全,但是在高并发情况下,同步操作会严重影响系统的性能。通过对三种解码器进行分析,CumulativeProtocolDecoder类相比于其他两类解码器具有较好的性能优势,因此本文通过实现该接口来自定义解码器。

通过对编解码的流程已经解码器的探讨,本文自定义了编解码工厂ServerProtocolCodecFactory类,工厂类中自定义了编码器ServerProtocolEncoder类,以及实现了CumulativeProtocolDecoder接口的解码器ServerProtocolDecoder类,分别实现了encode和doDecode两个接口方法来进行编码和解码的操作。各类关系图如图3-9所示。

图3-9 编解码类关系图

27

ServerProtocolDecoderServerProtocolEncoder-charset:Charset-log:Logger+encode()+ServerProtocolEncoder:Charset-charset:Charset-log:Logger+decode()+dispose()-memberName+ServerProtocolDecoder()+ServerProtocolDecoder():CharsetServerProtocolCodecFactory-decoder:ServerProtocolDecoder-encoder:ServerProtocolEncoder+getDecoder():ProtocolDecoder+getEncoder():ProtocolEncoder+ServerProtocolCodecFactoryCumulativeProtocolDecoder-BUFFER:AttributeKey+CumulativeProtocolDecoder()+decode:void+dispose():void+doDecode():boolean+removeSessionBuffer():void+storeRemainingInsession():void

编码器ServerProtocolEncoder根据字符转换协议将消息对象编码为二进制字节流,发送到网络中去。编码器的具体流程如图3-10所示。

结束Message==“error”?是创建IoBuffer对象,使用字节数组保存ASCII码传入Message开始否创建IoBuffer对象,使用字符定界法编码输出二进制到网络图3-10 编码器流程图

当有数据到达编码器需要编码时,由于报文协议格式有两种,一种是带有URL映射号的请求响应字符串报文,另外一种是返回“error”的字节数组报文,所以编码器首先需要判断接收的信息是“error”字符串,如果是则创建IoBuffer对象和5位大小的字节数组,字节数组每一位存放的是“error”每一个字符的ASCII码值,在将字节数组转为二进制输出。如果不是“error”字符串,则需要调用Mina内部使用的字节操作类IoBuffer提供的putString()方法将字符串转为二进制,同时需要手动添加换行结束符,并写入到到网络。

解码器的输入为原始字节流,NIO对字节流的原生处理方式是通过三个属性:position、limit、capacity来操作缓冲区的二进制数据。三个属性意义为:

(1)capacity:该属性描述了这个缓冲区可以存储的最大数据量,即Buffer最大存储元素数,这个值是在创建Buffer时指定的,且不会改变。

(2)position:Buffer实际上类似于一个数组,从网络通道读数据时,将读出的数据存放到底层的数组,Position用于指定下一个需要被写或读取的元素位置,用来跟踪截止目前为止已经写了多少数据,当初始化一个Buffer时,position初值为0。

(3)limit:初始化一个Buffer之后,在写模式下,limit表示能往缓冲区写入数据的最大值,当读取数据时,limit与写模式下的position位置重合,表示最多能够读取多少字节数据。

28

假设初始化一个IoBuffer对象,并设置缓冲区容量大小为10个字节,初始状态下position位置为0,limit与capacity被设置为9,在写入和读取的过程中capacity的值不会发生变化,position与limit会根据读写状态而更改位置,由于position置0,所以写入操作从缓冲区数组的第一个位置开始,并将数据写入到该位置,如图3-11所示。

图3-11 初始状态

从网络通道中读取字节数据,往缓冲区写入4个字节之后,position位置变为4,变为图3-12所示。

图3-12 写入4字节状态

在从写状态转为读状态之前,调用一次flip()方法,该方法将limit值设置到当前的position位置,将position值置为0,同时将position到limit之间的字节数据进行读取,读取完后切换到写入数据操作,此时状态如图3-13所示。

图3-13 读取完4字节状态

将缓冲区的数据读取之后,调用clear()方法将所有值设置为初始状态,即position为0,limit设置为capacity,状态如图3-14。

图3-14 clear之后的状态

29

本文自定义的解码器ServerProtocolDecoder参照上面的处理思想实现对二进制数据的解码操作,整个解码流程如下图3-15所示。

结束判断字节数是否等于5?是否创建字节数组读取数据否判断通道是否还有数据?是写入缓冲区1个字节原始消息数据开始获取数据总字节数否判断值是否等于10?是读取数据图3-15 解码器流程图

解码器实现解码的整个流程如下:

(1)原始消息数据到达解码器,获取当前原始数据的总字节数,判断是否等于“error”的5个字节数,如果相等说明这条消息是“error”,创建字节数组将其读取。

(2)如果总字节数不是5,则需要判断是否所有数据都已经写入缓冲区,如果没有,将一个字节写入缓冲区,进行判断该字节是否等于换行符’n’的ASCII码值10,如果相等则说明一段消息已经写入完毕,通过上面处理二进制字节的方式将换行符之前的字节从缓冲区读出,如果不等于10,则继续判断之后的写入操作。

(3)当网络中所有的数据写入缓冲区并被读取之后结束解析操作。

3.4消息代理模块

通过3.3节协议转换的实现,服务网关Zuul得到了Mina客户端请求的报文和数据,消息代理模块的主要功能就是将解析之后的请求和数据路由到对应的服务实例上,并获取返回结果传回到Mina客户端,因此需要对网关Zuul的路由转发原理进行分析研究,然后给出具体的实现方案。

30

3.4.1 网关Zuul路由转发原理分析

Zuul是Netflix OSS中的一员,它能够与Eureka、Ribbon、Hystrix等组件配合使用,当Zuul集成服务注册发现组件Eureka,以及在启动类上添加@EnableZuulProxy注解开启网关代理,就可以为Eureka中所有的服务进行路由操作,默认的转发规则是“API网关地址+访问的服务名称+接口URI”。其整体架构图如图3-16所示。

ZuulFilterRunner123Pre类型过滤器Route类型过滤器Post类型过滤器HTTP RequestZuulServletHTTP ResponseRequestContext图3-16 Zuul架构图

当有HTTP请求到达网关时,首先请求会被Zuulservlet处理,Zuulservlet相当于SpringMVC中的DispatchServlet的作用,所有的请求都通过它进行处理。Zuulservlet创建一个zuulRunner对象,并初始化RequestContext用于存储整个过滤器处理过程中请求的数据,同时被所有过滤器所共享,在过滤器之间传递数据。同时zuulRunner对象里还有FilterProcessor用于执行所有的ZuulFilter管理器,对所有的过滤器采用轮询方式的热加载,首先执行所有Pre类型的过滤器,然后执行Route类型过滤器,最后执行Post过滤器,如果在执行各个过滤器时发生了错误,就会执行Error类型的过滤器,当所有的过滤器执行完成之后,返回请求结果。

从网关Zuul的整体架构来看,其核心在于不同类型的过滤器。网关Zuul默认定义了4种类型的过滤器,当请求到达网关之后会被这4种不同类型的过滤器进行相应处理,并将响应结果返回给客户端,由此可见过滤器覆盖了整个请求的生命周期。网关Zuul的官方Wiki给出了关于请求的整个生命周期图[44],如图3-17所示。

31

HTTP RequestHttp ResponseHttp Request“pre”filters“routing”filters“post”filters“custom”filters“error”filtersOrigin Server图3-17 Zuul请求生命周期图[44]

整个请求生命周期过程中,请求首先会被pre类型的过滤器拦截处理,主要负责请求的路由映射以及校验等前置工作。通过pre类型过滤器找到请求需要转发的路由地址后,通过route类型的过滤器来完成对请求的转发工作,并将请求结果进行返回。通过post过滤器来获取请求信息和返回信息,并对结果进行相应的加工处理后返回给客户端。当上述三种类型的过滤器发生异常时会触发error类型的过滤器进行处理。

通过对pre类型和route类型核心过滤器的源码分析,发现网关Zuul的路由映射是使用的SpringMVC的处理逻辑,包含了SpringMVC两个核心组件:HandlerMapping映射器和HandlerAdapter适配器,SpringMVC的核心处理是首先将请求交给HandlerMapping处理器映射器,该映射器根据请求路径找到相应的HandlerAdapter处理器适配器,也就是处理请求业务逻辑的控制器或者过滤器,然后将请求返回,网关Zuul中也包含了相应的映射器和适配器。

Zuul初始化配置不是通过配置文件,而是通过@EnableZuulProxy注解引入配置,该注解源码如下。

@EnableCircuitBreaker

@Target({})

@Retention(E)

@Import({})

public @interface EnableZuulProxy {}

在注解接口中,里面引入了ZuulProxyConfiguration类,该类注入了与服务发现、负载均衡与请求转发等相关的过滤器,ZuulProxyConfiguration类继承于ZuulConfiguration,该类是Zuul的核心类,引入了相关的配置,其中包括了路由定位器RouteLocator,控制器ZuulController处理链路调用,32

ZuulHandlerMapping映射器,过滤器ServletDetectionFilter,过滤器管理配置ZuulFilterConfiguration等处理路由映射的核心类。通过这些核心类,网关Zuul处理请求路由映射的实现过程如图3-18所示。

dispatcherServlet捕获请求lookupHandler加载路由规则映射路由规则处理调用ZuulHandlerMappingZuulContollerZuulServlet图3-18 路由映射实现图

在初始化配置时,ZuulFilterConfiguration类将所有filter注册到FilterRegistry并初始化,ServletDetectionFilter是网关Zuul中pre类型的过滤器,它是最先被执行的过滤器,该过滤器总会被执行,主要负责检测当前请求是通过Spring的DispatcherServlet处理运行的,还是通过ZuulServlet运行的。一般情况下,发送到API网关的外部请求都会被Spring的DispatcherServlet处理,将请求转交到ZuulHandlerMapping映射器,该映射器继承了AbstractUrlHandlerMapping类,该类提供了HandlerMapping的映射机制,根据URL来查找处理器,该类中核心处理方法在lookupHandler里面,该方法中传入了一个urlPath参数和一个请求,首先判断urlPath是否被忽略,如果忽略这返回null,接着判断路由规则是否被加载或更新过,如果没有则重新加载,加载的过程是使用路由定位器routeLocator来获取路由规则,调用父类注册处理器,注册机制就是将路由信息提交给处理器,然后lookupHandler根据URL调用父类获取需要映射的处理器,该处理器使用的就是ZuulController类,也就是每一个路由规则的公共处理器都是ZuulController,最后这个处理器会调用ZuulServlet,该类中包含了三种类型的过滤器,并采用线性遍历的方式调用三种不同类型的过滤器,请求的最后转发依赖的是RibbonRoutingFilter和SimpleHostRoutingFilter两个route类型的过滤器。

RibbonRoutingFilter是route阶段第一个执行的过滤器,当配置文件中路由配置为path-serviceId类型时,有此过滤器进行转发,该过滤器集成了Ribbon,实现了客户端的负载均衡,是网关Zuul面向服务路由的核心实现,其底层实现原理是:1)通过Ribbon客户端负载均衡工具获取服务注册中心可用的服务实33

例清单;2)根据默认的轮询负载均衡算法选择一个可用的服务实例,获取其IP、端口等信息;3)默认使用Apache的HttpClient转发HTTP请求到具体服务实例。

SimpleHostRoutingFilter是route阶段第二个执行的过滤器,该过滤器主要是对配置文件中路由配置为path-url的请求进行转发,它的执行逻辑是直接向配置文件中的URL的物理地址发起请求,所以无法做到负载均衡,源码中通过buildZuulRequestURI()方法重构URL之后,使用Apache的CloseableHttpClient来发送重构好的HTTP请求并将返回结果保存在RequestContext上下文中。

RibbonRoutingFilter和SimpleHostRoutingFilter底层对请求转发的原理都是通过路由规则对URL进行重构,然后借助Apache的HttpClient工具发起请求转发到具体的服务实例上,本文所设计的消息代理模块其主要功能也是为了能将解析后的请求转发到服务实例上,因此可以借助Apache所提供的HttpClient来实现该模块功能。

3.4.2 消息代理模块的设计与实现

消息代理模块的设计与实现首先需要考虑:消息代理模块的功能是将协议转换之后通过映射表获取的URL进行转发,如果该URL指向具体的服务实例,就会和网关Zuul中的SimpleHostRoutingFilter过滤器一样无法做到负载均衡以及服务实例的高可用性,因此消息代理模块必须把请求转发到网关Zuul上,在通过网关面向服务的路由方式去访问具体的服务实例,消息代理模块的处理流程如图3-19所示。

Mina客户端发送请求二进制字节流协议转换Zuul-Mina服务器协议转换模块消息代理模块微服务系统解析后的URLHttpClient重构的URL请求负载均衡访问具体服务实例响应内容响应内容响应内容响应内容图3-19 消息代理模块处理流程时序图

34

从流程时序图可以看出,消息代理模块核心在于使用HttpClient工具将解析之后的URL进行重构,并转发到服务网关。HttpClient是Apache Jakarta

Common下的子项目[45],提供了高效的HTTP协议客户端编程工具包,通过加入相关依赖就可以调用他的工具方法。本文通过自定义类HttpUtil来调用HttpClient工具实现Get和Post请求,该类实现的关键代码如下所示:

CloseableHttpClient Requestproxy = Default();

URIBuilder builder = new URIBuilder(url); // 创建url

if (param != null) { ameter(key, value); } //添加参数,

URI uri = (); //对url进行重构

HttpGet requestGet = new HttpGet(uri); // 创建http GET请求

response = Requestproxy .execute(requestGet ); // 执行请求

if (tusLine().getStatusCode() == 200) {

resultString = ng(ity(), "UTF-8");}

Requestproxy .close();

上面的代码是实现Get请求的关键代码,首先创建了一个Requestproxy 对象,根据传入的url创建一个URIBuilder对象,通过该对象可以调用addParameter()方法向url中添加参数和信息,再调用build()对修改之后的url进行重构。通过HttpGet创建请求方法实例,并指定请求url,表明这是一个Get请求。最后通过调用Requestproxy 对象的execute()方法将重构后的URL进行转发,该方法返回一个Response,并通过getEntity方法可获取包装了微服务实例响应内容的对象。最后还需要调用close()方法释放连接。

由于消息代理模块是将解析后的请求发送到服务网关Zuul,再通过网关配置的path-serviceId的匹配规则进行路由,所以应该对urlmap映射表中的URL格式进行统一化管理,这样更有利于请求转发HttpUtil对URL进行重构,该映射表的格式如下:

<?xml version="1.0" encoding = "UTF-8" ?>

四位字符串

网关IP:网关端口/服务名/接口URI

35

3.5 本章小结

本章主要是对第2章提出的基于微服务网关Zuul的TCP功能扩展的设计与实现,对实现该功能扩展的三个模块分别进行了设计与实现。在3.1节首先对网关的整体结构进行了设计,分析了各模块的主要功能。3.2节首先对服务网关Zuul的启动过程和Mina服务器的设计原理进行了分析,以此对Zuul-Mina服务器模块进行了设计与实现。3.3节主要从三个方面对协议转换模块进行了设计与分析:1)设计了Mina客户端与微服务系统之间的通信架构;2)自定义了通信过程中的报文格式与映射表;3)自定义了编解码器以实现协议转换。最后3.4节对网关Zuul的路由转发的原理进行了分析,并利用其原理对消息代理模块进行了设计与实现。

36

第4章 基于服务网关Zuul的令牌桶限流算法改进

本章在Spring Cloud框架的基础上,结合网关Zuul,首先对传统令牌桶限流算法进行改进,并将改进后的算法应用到了服务网关中为其提供限流功能。接着实现了限流开关的动态更新,对限流策略进行了优化。本章首先分析了传统令牌桶算法的原理和不足之处,接着对网关Zuul的自定义过滤器的原理以及对限流工具类RateLimiter进行分析,提出了改进方案,最后结合Zuul对令牌桶算法的改进以及限流开关的动态更新进行了实现。

4.1 传统令牌桶算法原理分析

令牌桶算法是网络流量整形和速率限制中最常用的一种算法,其核心在于通过大小固定的令牌桶来控制对流量的处理。令牌桶算法可以以恒定的速率不断地产生令牌将其存入桶中,如果桶中令牌不被消耗或者被消耗的速率小于产生令牌的速率,令牌桶中的令牌就会不断增加,直到达到令牌桶的容量,超出的令牌将会溢出。输入数据包到达令牌桶消耗相应的令牌数之后流出,如果桶内没有足够的令牌进行提供,则该数据包将会在桶内一直等待,直到桶内产生了足够的令牌。因此通过合理的设置阈值,令牌桶算法能够允许一定的突发流量,限制数据的传输速率并以峰值速率发送流量。令牌桶算法基本思路如下:

(1)将令牌以每秒r个的平均速率放入桶中。

(2)桶中最多存放b个令牌,如果桶满了,新放入的令牌会被丢弃。

(3)当一个n字节的数据包到达时,消耗n个令牌,并转发该数据包到网络中。

(4)如果桶中可用令牌小于数据包所需的n个令牌时,不删除桶中令牌,将该数据包标识为流量限制之外。

对于流量限制之外的数据包,一般可以采取三种不同的处理方式:

(1)将数据包直接丢弃。

(2)将数据包放入队列中进行等待,当桶中产生了足够的令牌之后,再将该数据包进行转发。

37

(3)将数据包直接转发到网络中,同时需要对数据包做特殊标记,当网络过载的时候需要将这些被特殊标记的包丢弃。

令牌桶算法的思路如图4-1所示。

R 令牌数/秒等待令牌N 位数据包流出桶令牌桶做多容纳b个令牌令牌数

从上图可以看出,传统的令牌桶算法不足之处在于对请求数据包进行令牌分配的时候,并没有对请求进行过滤,只是判断桶中是否有足够的令牌进行分配,如果桶中令牌数量不够,则会将不能满足条件的所有请求丢弃,其中就会包含一些核心请求比如下单支付转账等请求,导致这些请求不能正常或即时得到响应,影响用户对整个服务系统的体验感以及高并发情况下核心服务的高可用性,因此对请求进行过滤处理是改进传统令牌桶算法的切入点。

4.2 传统令牌桶算法的改进

4.2.1 改进方案的提出

通过第2章和第3章中对网关Zuul的工作流程和原理分析,了解到网关Zuul的核心是一系列的过滤器,这些过滤器按照类型能够对请求进行不同处理,同时,网关Zuul还提供了两种自定义过滤器的方法:1)通过实现Filter接口,并重写其中的init、doFilter、destory、run等方法进行实现;2)只需要继承ZuulFilter抽象类,实现其定义的四个抽象函数即可对请求进行拦截和处理,四个抽象方法和含义与功能如下:

(1)filterType:该方法通过返回一个字符串来标识该过滤器的类型,这个类型就是在本文3.4.1节中所介绍的HTTP请求过程中的各个阶段,其中包括了四种不同类型的过滤器,如果该方法返回的是pre,表示该过滤器在请求路由之38

前被调用;返回的是route,表示该过滤器是在请求转发的过程中执行;返回值是post,表示该过滤器在pre和route类型过滤器之后被执行;返回值是error,表示该过滤器只在处理请求过程中发送错误才会调用。

(2)filterOrder:该方法通过返回一个整数值来定义过滤器的执行顺序,数值越小表示该过滤器优先级越高,越早被执行。

(3)shouldFilter:该方法返回一个boolean值来判断当前过滤器是否要执行,通过此方法我们可以设计过滤器的开关及有效范围。

(4)run:该方法负责过滤器的具体处理逻辑,通过此方法可以实现自己定义的过滤逻辑,对请求进行拦截过滤或者对返回结果进行加工处理。

同时,RateLimiter类为网关Zuul提供了令牌桶算法的接口使用。RateLimiter是Google开源工具包Guava提供的限流工具类[46],该类基于令牌桶算法来完成限流。同时Spring- Cloud -Zuul-RateLimiter包结合Zuul对RateLimiter进行了封装,因此网关Zuul可以通过导入RateLimiter依赖来使用令牌桶算法,并在此基础上对其进行改进操作。RateLimiter主要向网关Zuul提供了三个接口方法:

(1)create(int permits)

该方法目的在于创建一个稳定输出令牌的RateLimiter,通过指定permits值保证平均每秒不超过permits个请求,也就是每秒往令牌桶中添加permits个令牌。当请求到来的速率超过了permits值时,能够保证每秒只处理permits个请求,当请求到来的速率小于permits时,RateLimiter最多也只能囤积permits个请求。

(2)acquire(int permits)

该方法表示从RateLimiter中获取permits个令牌,如果不带参数默认是获取一个令牌。如果没有获取到令牌,则一直等待,直到获取到足够的令牌。返回值是等待的时间,以秒为单位。

(3)tryAcquire(int permits, long timeout, TimeUnit unit)

该方法判断在timeout时间内,是否能够获取到permits个令牌,如果能则

返回true,如果不能则直接返回false,不在等待获取令牌。

通过网关Zuul提供的自定义过滤器功能以及RateLimiter类提供的令牌桶算法使用接口,本文结合网关Zuul在传统令牌桶算法基础上进行改进,首先网关Zuul通过自定义过滤器拦截所有请求,在请求获取令牌之前插入了一个基于URI配置文件的拦截策略,通过该拦截策略对当前URI进行判断,根据不同的判断结果做出相应的后续操作。该URI配置文件包含了所有不需要限流操作的39

核心URI,同时以XML的配置文件形式保存在网关服务系统中,改进后的令牌桶算法思路如图4-2所示。

R 令牌数/秒网关ZuulN 位数据包基于URI配置文件的拦截策略不存在等待令牌流出桶存在流出桶令牌数

改进后的算法流程如图4-3所示,具体步骤如下:

(1)按照特定的速率向令牌桶中投放令牌。

(2)请求到达网关Zuul之后,获取当前请求URI,匹配当前请求URI是否存在于配置文件中,如果存在就不进行限流操作,直接转发到网络或者后续过滤器,如果URI配置文件中不存在当前请求,则需要等待获取令牌。

(3)等待获取令牌的请求如果能够从令牌桶中获取到足够的令牌,就直接流出令牌桶,如果超过了阈值则该请求被拒绝访问。

开始是否开启限流操作?是获取当前请求URI否URI是否在配置文件中?是否等待获取令牌否是否超过了阈值?允许请求访问是拒绝请求访问结束图4-3 改进后的算法流程图

40

4.2.2 改进的具体实现

结合网关Zuul实现令牌桶限流算法改进的过程主要分为两步:

(1)首先在网关服务端添加RateLimiter的依赖,以支持Zuul调用它所提供的接口方法实现令牌桶算法限流操作,关键代码如下:

guava

18.0

(2)自定义过滤器类LimitFilter实现对令牌桶算法的改进,继承ZuulFilter并实现其四个抽象方法,该类核心代码如下所示:

public static volatile RateLimiter rateLimiter = (10); //设置速率public String filterType() { return "pre"; } //在请求路由之前执行该过滤器

public int filterOrder() { return -1; } //优先级较高

public boolean shouldFilter() { return true; } //该过滤器必须执行

public Object run() {

RequestContext ctx = rentContext(); //获取当前请求

String uri = uest().getRequestURI(); //获取请求URI

.........

Document document = (""); //使用DOM解析配置文件........

(eValue()); //取出配置文件中核心URI到List

........

if(ns(uri)){ //存在于配置文件中,不需要对其限流

(uri+" exists in the configuration");

}else {

(uri+" is not in the configuration"); //不存在则需要限流

e(); } //请求获取令牌,令牌不足时进行限流

}

41

4.3 限流开关的动态更新

通过对4.3节实现代码分析可以看出,该过滤器的shouldFilter方法返回的是true值,即默认限流的功能一直启动着,当并发请求访问量很小的时候,该限流过滤器也会对每个请求判断一次是否在配置文件中,同时也会对配置文件解析一遍,这样会导致服务网关系统性能下降,所以需要实现限流开关的动态更新。

结合本课题来源,通过监控虚拟燃气供应商平台下的各系统每天的请求访问量,发现流量的高峰期主要集中在上午的9点到11点,下午的3到5点,因此可以通过重写shouldFilter方法,根据时间段的设置与当前时间节点的变化来返回不同的boolean值,判断该过滤器是否执行,动态的开启和关闭限流功能。关键实现代码如下:

public boolean shouldFilter() {

Calendar cal = tance(); //获取当前时间

int hour = (_OF_DAY); //获取当前24进制小时

if ((hour>=9&&hour<=11)||(hour>=15&&hour<=17)) //设置时间段

{

return true; //该时间段返回true,启动限流功能

}else

{

return false; //不是该时间段,不启动限流操作

}

}

4.4 本章小结

本章主要实现了基于服务网关Zuul的令牌桶限流算法的改进,在4.1节对传统令牌桶的算法思路进行了分析,指出其不足指出;然后在4.2.1节中提出了对令牌桶算法改进的方案,并在4.2.2节中对令牌桶算法的改进进行了具体实现;最后4.3节在改进的算法基础之上,实现了限流开关的动态更新。

42

第5章 实验验证和结果分析

本章主要对第3章基于服务网关Zuul的TCP功能扩展以及第4章限流算法改进进行功能测试和性能实验。

5.1 TCP功能扩展模块测试

本节主要针对第3章基于服务网关Zuul的TCP功能扩展模块进行功能测试,由于其扩展模块实现的是Mina客户端与Spring Cloud微服务系统之间的通信,所以先对Spring Cloud各微服务系统进行项目环境的搭建,在分别对Zuul-Mina服务器模块、协议转换模块、消息代理模块三个模块中的各个功能节点进行测试。

5.1.1 功能验证实验环境搭建

对网关Zuul的TCP功能扩展的功能测试将在一台windows机器上完成,开发工具使用Intellij IDEA 17.0.4进行代码编写,通过Maven进行项目结构管理,测试环境以及各软件版本如表5-1所示。

表5-1 测试环境说明

操作系统

64位windows7旗舰版

CPU型号

A8-5550M

**********内存 java

SpringBoot

1.5.9

Spring

Cloud

Edgware

Mina

8G 1.8.0_922.0.7

测试环境搭建好之后,需要完成项目的搭建,下面简述项目搭建的主要过程。

(1)创建一个Maven项目SpringCloud-Mina,在该项目中创建4个Spring

Boot应用模块:服务注册中心模块eureka-server、服务网关eureka-zuul、服务提供者eureka-client、服务消费者eureka-client1。项目结构如图5-1所示。

43

基于微服务网关Zuul的TCP功能扩展和限流研究

本文发布于:2024-01-31 10:31:39,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170666829927879.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:服务   进行   网关   请求   实现   限流
留言与评论(共有 0 条评论)
   
验证码:
排行榜

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23