第36卷 第2期 福 建 电 脑 Vol. 36 No.2
2020年2月
Journal of Fujian Computer
Feb. 2020
———————————————
黄永华,男,1985年生,本科,主要研究领域为Web 应用、微服务架构。E-mail: jlis@qq 。
应用Socket 的微服务之间的通讯
黄永华
(厦门海迈科技股份有限公司研发中心 福建 厦门 361008)
摘 要 通常微服务之间的通讯是通过HTTP 协议交互的,而HTTP 协议是无状态、短连接的。采用基于Socket 编程接口能够监听各种微服务的状态,实时处理各类消息,通过心跳维持微服务在线状态,保证微服务连接的有效性。当微服务离线时,Socket 连接即被断开,服务端能够接收到该离线消息,再转发至其
他微服务;其他微服务收到离线消息,就能够触发如服务发现注册列表更新、网关接口更新等事件。当建立消息服务通讯协议后,甚至可以将服务发现取代,缓存各类接口信息,使微服务集能够更有效地协同工作。 关键词 微服务通讯;消息通讯;Socket 通讯
中图法分类号 TP31 DOI:10.16707/jki.fjpc.2020.02.018
Communication between Micro-services Based on Socket
HUANG Yonghua
(R & D Center, Xiamen Hymake Technology CO., LTD, Xiamen, China, 361008)
1 采用HTTP 存在的问题
在微服务中[1],较为典型的HTTP 协议应用场景为服务发现(Eureka Server )与客户端(Eureka Client )之间的信息交互[2]。其相关参数配置,如表1所示。
表1 Eureka 参数配置说明
参数
描述
registry-fetch-interval-seconds 获取服务列表注册信息,默认30秒
lease-expiration-duration-in-seconds 等待下一次心跳的时间 lease-renewal-interval-in-seconds
客户端发送心跳数据的间隔时间
enable-self-preservation 当超过该时间服务发现将注销客户端
eviction-interval-timer-in-ms
清除无效节点的时间间隔
socket通信报文格式通过参数可知,为了兼容性能,参数不能设置过小;为了时效性,参数也不能设置过大。
HTTP 协议是无状态的,它是短连接,浏览器和服务器每进行一次HTTP 操作,就建立一次连接,
当任务结束就中断连接。也可以说,短连接是指
Socket 连接后发送后接收完数据后马上断开连接。
而基于Socket 的长连接[3],能够建立稳定的通讯通道,保证消息实时持续交互,且它是有状态的。
若进行大量数据并频繁交互需要耗费大量的资源。而常见的微服务集通常采用HTTP 协议,很多监控与交互很难达到实时。
2 基于Socket 的解决方案
采用Java NIO(Java non-blocking IO)[4] 的传输TCP/IP ,它是Socket 通讯的实现。
通常情况下,Socket 连接使用TCP 协议,因此Socket 连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。
而HTTP 连接使用的是“请求—响应”的方式,每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。
为了实现高效的数据传输、解析、加密/解密,使用字节码进行通讯。由于TCP 传输数据是没有边界的,所以需要定义封包,用来解析数据。
2020年福建电脑69
2.1基础数据传输
2.1.1 封包设计
为了能够实现高效传输,定义封包大小最大长度65536(64KB)。封包大小,即头部4个字节代表整个封包长度,接下来2个字节代表命令,最后部分即为数据负载,如图1所示。
图1 封包结构图
2.1.2 粘包
当封包内容较少,TCP/IP协议报文与报文之间是没有分界符号的,但它能够有序地传入数据,可能会造成粘包,粘包需要拆分,如:00 00 00 06 00 01 00 00 00 06 00 01 。根据封包定义,首先获取第一个封包数据长度6,接着读取后续的2(6-4)个字节,构成完整封包(数据负载为0字节),第二个封包同样是6个字节,依此类推。
2.1.3 拆包
在传输过程中,可能会将一个封包拆成多块传输。这时,需要等待后续数据接收完毕,再进行拆包,假定第一次收到数据:00 00 00 06 00 。根据封包定义,该封包长度为6字节。但是,只收到5个字节,无法进行有效拆包,此时等待后续数据如灰部分:00 00 00 06 00 01 00 00 00 。此时,由于获取到足够的数据(大于等于6字节),因此,将数据拆包,剩余数据:00 00 00不够4个字节,无法获取长度(
需要4个字节),继续等待数据。当获取长度后,若小于封包长度即等待继续接收数据,若大于等于封包长度,即可拆包。
2.1.4 丢包
当获取到的数据出现问题,无法解析有效的封包长度,即丢失数据(注:TCP/IP协议是不丢包的),此时,客户端无法解析数据,断开与服务器连接,再次与服务器建立新的连接。
2.1.5 Hash校验
为了保证封包的有效性,可以加入Hash值,对整个封包持续校验,引入校验:
对所有的数据增加与、或校验,获取到2字节校验码,检验数据的有效性。若中途增加了一个额外的封包,没有有效的校验码,服务端接收到的数据无法通过,则与客户端断开连接。而该校验规则是持续的,每发送一个封包,都参与到校验规则中。
图2 封包结构图(校验)
2.1.6 加密解密
通常为了数据安全性,采用AES OFB加密解密。当客户端连接时,获取到加密解密密钥(含随机值),服务端使用加密密钥加密数据,客户端使用解密密钥解密数据。同理,客户端使用加密密钥加密数据,服务端使用解密密钥解密数据。每次发送的封包,都参与解密。若解密失败,无法获取有效封包,则断开连接。如服务端加密数据:
图3 加密过程
反之,客户端收到,则解密。
2.2微服务通讯
根据封包的结构体,定义如表2所示的命令,便于微服务之间进行通讯。
表2 通讯代码表
功能命令描述
心跳00 01 心跳服务,当服务端检测到客户端空闲时,
发送该心跳内容,若客户端在有效的时间
内,没有响应,则断开连接。
初次握手00 02 初次握手,返回加密解密密钥(可能含有
服务端版本信息)
在线列表00 03 握手之后,获取在线列表
在线状态00 04 用户上线/离线通知
消息00 05 系统向所有用户发送消息
2.2.1心跳
心跳是保证正常通讯的一种手段。服务端判断客户端处于空闲状态时,就会发起心跳检测。若虚假连接,没有回复心跳信息,则自动断开与客户端连接。通常,客户端收到心跳信息,须立即返回信息数据,携带的数据内容通常为时间戳。
70 黄永华:应用Socket的微服务之间的通讯第2期
2.2.2 初次握手
当客户端连接服务端时,会收到服务端的版本信息以及加密/解密密钥。后续通讯,采用密钥对传输数据进行加密/解密。
2.2.3 在线列表
当客户端A连接服务端后,会自动获取在线客户端列表;当客户端B连接服务端后,或者在线列表就包含了A客户端信息。
2.2.4 在线状态
当客户端A上线后,其他客户端如B,会收到由服务端发送的在线信息;当客户端A下线后,客户端B会收到来自服务端的下线消息。
2.2.5 消息
消息,是微服务核心功能。负责微服务与微服务或消息中心之间的通讯。消息分为2种类型,一种广播,由系统向客户端发送消息;一种私有消息,通常需要回复。
采用Java异步线程完成消息通讯。
如图4所示,带有回复的消息过程为:
a.客户端A向消息中心发送数据;
b.消息中心向目标客户端B转发数据;
c.客户端B完成数据处理,返回数据;
d.消息中心转发返回数据给客户端A。
图4 普通消息过程
如图5所示,带有确认的消息回复过程为:
a.客户端A向消息中心发送数据;
b.消息中心向目标客户端B转发数据;
c.客户端B处理数据,并向消息中心发送询问消息;
d.消息中心将询问消息转发给客户端A;
e.客户端A处理该消息,并返回消息中心;
f.消息中心将数据转发客户端B;
g.客户端B收到询问数据后,发送处理后的数据;
h.消息中心转发返回数据给客户端A。
图5 消息询问过程
2.3 微服务之间的通讯
2.3.1 应用集
网关(类型:GATW AY)、服务发现(类型:DISCOVERY)、微服务应用(类型:CLIENT)通过Socket方式连接至消息中心[5]。当微服务应用2连接时,服务发现将收到网微服务应用2的上线消息并写入服务发现,服务发现将相关信息转发网关,网关重新获取路由信息。
图6 消息中心
2.3.2 消息中心集
当应用集足够大,单消息中心很难满足通讯需求时,消息中心升级为集。通过集,提高消息中心数据的处理能力。每个消息中心都有唯一标识domain,每个微服务连接消息中心将自动注册账户account,消息通讯的ID即account@domain。例如,消息中心1 域为domain1,消息中心2 域为domain2,消息中心n 域为domainn,应用微服务客户端account为client,那么完整的ID为client@domain2;应用微服务客户端2account为client2,那么完整得ID为client2@domainn。
每个消息中心都拥有相互转发消息的能力,并
2020年福建电脑71
且相互注册。即消息中心1注册到消息中心2,ID
为domain1@domain2。当消息中心1,收到客户端
消息发送目标client2@clientN时,将消息转发至消
息中心N,再由消息中心N转发应用客户端2。
图7 消息中心集
3方案应用效果
在微服务架构中,引入Socket作为通讯方式,时效性得到了巨大提升。
由于Socket的通讯是长连接,即断开或者连接能够实时的被发现,从客户端上线到服务端接收再到客户端请求或者从客户端与服务端断开连接的这个过程的数据传输时间损耗即为它的时效性。
本文定义了消息心跳包,心跳包内容携带时间戳。通过发送前获取的系统时间戳与接收后的当前的系统时间戳进行比较,计算出时间间隔。取该时间间隔的一半,即为微服务使用Socket后进行信息交互的时间消耗。如图8所示。
图8 心跳代码示例
sendPing获取系统当前的时间戳并发送,pongReceived 接收到客户端返回的时间戳,获取系统当前的时间戳减去接收到的时间戳,即为心跳过程的时间消耗。
通过测试,获取到数据传输的整个过程时间:小于等于1毫秒,如图9所示。
图9 心跳代码运行结果(时间毫秒)
根据以上数据,服务发现与客户端之间的信息交互,采用HTTP与Socket这两种方案的参数比较,如表3所示。
表3 Eureka采用HTTP与Socket比较
功能命令描述registry-fetch-interval-seconds 30s <= 0.0005s
lease-expiration-duration-in-seconds 5s <= 0.0005s
lease-renewal-interval-in-seconds 5s <= 0.0005s enable-self-preservation 5s <= 0.0005s
eviction-interval-timer-in-ms 5s <= 0.0005s
从表3可以看出,采用Socket方案比HTTP方案在时效性方面提升10000倍以上。
4总结
采用Socket通讯,可实现实时地监听集中各应用状态或实时获取服务注册信息。当微服务断开Socket连接,服务发现能够实时收到用户消息,重新更新微服务列表缓存,避免了微服务需要等待一个心跳周期。当服务发现更新完缓存后,转发消息至网关,网关接收微服务离线状态,再从服务发现更新微服务列表,同时也能够实时清除无效节点。
当然,更好的解决方案是建立一个消息服务中心集。所有的微服务全部注册到该集上,这样可以减少网关与服务发现的交互,从网关客户端之间获取微服务信息,完成网关路由。
参考文献
[1] Pivotal 团队, spring.io/projects/spring-boot, 2019,7,13
[2] acle/javase/8/docs/api/java/net/Socket.html, 2019,
7, 13
[3] github/Netflix/eureka/wiki, 2019,7,13
[4] JSRs, /en/jsr/detail?id=51, 2019,7,13
[5] Pivotal 团队
, spring.io/projects/spring-cloud, 2019,7,13
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论