N年前曾有一个架构设计的案例。
一、需求
【背景】
当时集团有10万名一线员工,每名一线员工工作都手持一个基于Android系统定制的移动设备(下文简称A设备)。类似于手机,通过SIM卡可进行网络通信。其上安装多个应用,支持多种业务。A设备其中一个附件设备可支持刷卡支付业务,简称此应用为pos应用。另有一个用于无卡支付的应用,简称为pay应用。由于发展历程的原因,两个应用都是符合ISO8583协议要求,但对8583部分可自定义的字段由于业务场景的不同,有不同的自定义实现。而且这两个应用由不同的开发团队实现,无法兼容。A设备除了pos应用、pay应用还部署了很多集团旗下其它子公司的应用。pay应用、pos应用作为前端,其对应的后端应用系统也是不同的。而根据业务发展趋势,后续的业务类型会更多,而自定义的8583报文协议已经显示出力不从心。特别是从3G网络升级到4G网络,A设备承载的业务将增加图像等多媒体信息。
【需求】
由于集团发展战略的需要,要进一步加强终端设备的管控,将只允许A设备通过https直接接入集团私有云,再由集团的私有云接到原有机房服务器。如何设计一个改造方案,能以尽可能低的成本完成集团要求并支持未来发展需要?
原有的部署:pos app / pay app ----( 8583协议基于tcp短连接 )----> bff ----(hessian rpc ) ----> ms 。
要求的部署:A设备(pos app / pay app) ---- ( https ) ----> 集团的私有云 ---->公司 SF网络区。
【补充】
8583协议是基于ISO8583报文国际标准的包格式的通讯协议,8583包最多由128个字段域组成,每个域都有统一的规定,并有定长与变长之分。8583包前面一段为位图,它是打包解包确定字段域的关键。8583协议都是请求响应式,没有会话状态关联多个请求。客户端发起一个请求,服务器端对应的接收请求,验证请求,处理请求,响应结果。8583报文是二进制数据的格式。
bff是一个Backend for Frontend前置网关性质的应用系统,负责解释8583报文,编排串联并调用rpc微服务,再将rpc微服务响应的结果,编码成8583报文响应给前端。前端app 通过8583协议访问bff前置系统。而其中的8583协议是BFF基于mima框架TCP短链接实现的,TCP有SSL安全层。一次请求与响应,就对应于一次TCP连接的打开与关闭。bff前面有F5负载均衡器。
ms在这里是指后端的多个微服务架构风格的应用系统,支持hessian 协议,micro-service-a, micro-service-b, micro-service-c... 。bff与ms都部署在公司Server Farms 网络区,此网络区与私有云有专线连接。
二、分析
- pay app + pos app 需要支持数千的TPS。
并发请求不是太高。但A设备有10万,维持10万的长连接成本较高,是没有太多必要的。 - 8583报文最大不超过10KB。
对私有云至SF网络区的专线带宽有些要求。 - 基本属于重构的范围,不涉及业务功能的变化。但需要支持发展趋势。一是一线员工需要假设一定的增长速度。二是随着A设备全部升级为4G网络,能承载的业务也更多种多样,8583报文的灵活性不及restful。
- 已自定义的8583报文,按MESSAGE_TYPE+PROCESSING_CODE计算,包括请求与答复,按1000类报文评估。
如果全部都直接改造系统代码,改为restful风格的URL形式,显然是开发与测试成本极大的。想要更低成本的解决方案,就必须保留8583报文原本的编码解码逻辑,将其报文内容通过https传输。
三、架构方案
【设计思路】
基于以上需求分析,架构方案的主要思路是:8583 over http。
将8583二进制报文内容基于https协议传输。即在HTTP服务器的配置文件中增加一种自定义的MIME( Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。例如content-type可命名为"application/pay8583", ”application/pos8583“。只要自已的http服务器能识别即可。如果http服务器不方便修改,采用application/octet-stream的content-type也是可行的。但需要另外定义一个http头部字段标识具体是哪一种8583自定义报文格式。
在集团私有云部署3台Nginx服务器,使其成下面的访问链路:
A设备(pos app / pay app) ---- ( 8583 over https ) ----> 私有云Nginx ----(8583 over http)----> bff ----(hessian rpc ) ----> ms 。
【pos app / pay app 改造】
- 将原本的TCP(SSL)连接改为 https连接。按下文Nginx要求,增加请求及响应时的http首部字段。
- 8583报文不需要改动,以二进制格式通过https传输。
【私有云部署Nignx】
Nginx前可利用私有云已有的负载均衡器(例如可能的LVS-DR + Keepalive)。
修改Nginx配置文件 mime.types。使其支持content-type "application/pay8583",对于自定义的content-type,只支持post请求方法,不支持GET/PUT/DELETE/HEAD等请求方法。
修改nginx.conf 的http配置块,修改keepalive_timeout保持与前端一定时长的连接。
修改nginx.conf 的server配置块,配置https ssl_certificate。Nginx可以卸载SSL层,转换为普通的http长连接给到BFF。
修改nginx.conf 的location配置块,proxy_pass。
根据context反向代理至对应的bff。例如 https://domain-name/pos 转发至pos-bff,https://domain-name/pay转发至pay-bff,https://domain-name/biz-new转发至新的业务。不同的context可以对应到不同的前端app/ 不同的业务类型 / 不同的子公司。biz-new可以采用新的restful风格。domain-name/pay的业务可以在未来长期的系统功能迭代过程中,慢慢的逐步的将8583报文转换为restful风格。如果原有业务长期没有变化,则保持为8583格式。保持与BFF的长连接。
proxy_http_version 1.1;
proxy_set_header Connection "";
keepalive 16;
- 转发客户端真实IP
proxy_set_header X-Real-IP $remote_addr;
请求时,Nginx应该转发这些首部字段: Host, Accept, User-Agent, Connection, Keep-Alive, Content-Type, Content-Length。
响应时,Nginx应该转发这些首部字段:Connection, Keep-Alive, Cache-Control,Content-Type, Content-Length。
【BFF改造】
需要增加一个DispatcherServlet,用于从http post的request body获得8583报文,转交给BFF原有的解码器。
增加一个Header8583Filter,用于检查下面 8583 over https 设计中所要求的 HTTP 首部字段, 不合法时返回 400 状态码, 合法时设置响应首部。
Accept应该为”application/pay8583“,User-Agent应该为”<device_name>-<app_name>-version“,Content-Type应该为”application/pay8583“,Cache-Control应该为” no-cache“。
当BFF编排微服务, 发生了 Hessian 超时或其它意外异常时, 应当响应500状态码。 严禁将服务器异常堆栈信息返回终端设备。待上线验证没问题后,逐步移除mina 框架。 改造后的方案不再使用 mina 框架, 而是利用了 Servlet 容器对于 Http 协议的支持。并且原本的大量的 TCP 短连接,已有私有云服务器 nginx 承接,转变为少量的 http 长连接。
禁用Cookie,BFF不需要HttpSession。
8583报文不需要改动,后续ms都不需要有任何改动。最大限度的减少开发的工程量。