基于OpenResty和Node.js的微服务架构实践

2017全球顶级技术盛会Qcon在北京成功举行。个推平台研发部总监、首席架构师俞锋锋在大会上的《基于OpenResty和Node.js的微服务架构实践》主题演讲,可谓干货满满,为与会者开辟了技术开发的新思路。今天小编就带领大家,一起走进微服务的世界。(本文根据速记稿整理而成,略有删减)

 

什么是微服务?


传统的单体服务架构是单独服务包,共享代码与数据,开发成本较高,可维护性、伸缩性较差,技术转型、跨语言配合相对困难。而微服务架构强调一个服务负责一项业务,服务可以单独部署,独立进行技术选型和开发,服务间松耦合,服务依赖的数据也独立维护管理。虽然微服务存在部署复杂、运维难度较大、分布式事务控制难、容错要求高等缺点,但总体而言,微服务的优点远大于其复杂性。




 

微服务架构需要注意哪些问题?


微服务架构,首先考虑客户端与服务端之间的通信问题。有两种解决办法,一是客户端与多个服务端直接进行通信,但存在对外暴露接口细节、众多接口协议无法统一、客户端的代码复杂、服务端升级相对困难等问题。二是客户端访问统一的API网关,由API网关调用众多服务接口,易实现统一通信协议,降低客户端和服务端代码耦合,也可以达到统一的鉴权和流控,然而此方式存在延时增加的风险,可能使API网关成为系统发展的瓶颈。


微服务架构是分布式服务架构,如何进行服务的注册和发现也是需要解决的问题。一种是通过客户端发现,调用方需要知道所有依赖服务的地址,开发成本较高,协议升级比较困难。另一种是通过统一网关发现具体服务的地址,对客户端来说实现比较简单,能够在网关进行统一鉴权和流控,要求网关高度可用性。




调用微服务尽可能做到有序,避免相互调用,杜绝循环调用。服务间要有清晰的层次关系,上层服务可以依赖下层服务,如果遇到下层服务需要同步消息给上层调用方的情况,可以考虑通过消息队列异步解耦,比如订单/审核系统在创建订单或者改变订单状态时,可以考虑使用双写(即写入数据库后,同时在消息队列也写一份),异构系统(比如订单执行系统)可以通过订阅消息保存异构数据。

 

 

个推在微服务上有哪些实践?


个推的服务场景主要有三种。其一是个推推送场景,通过Java语言开发,SOA的架构方式来实现,保证信息推送的实时性与高并发性,这块微服务改造比较困难;其二是广告交易平台,比如投放平台、DMP数据管理,以Java为主进行开发,对并发数要求较高,我们在逐步进行基于Java的微服务框架的微服务化尝试;其三是web业务系统,它为前端提供无状态的业务API接口,是典型的request/response方式,同时,这是我们目前微服务实践最多的场景。


随着业务快速发展,公司web相关的业务系统开发需求不断增加,这些系统都涉及到用户管理,后台管理,权限管理等。为进一步提高团队开发效率,我们对服务进行平台化、模块化改造,选择了如上的技术选型。


个推的整体架构如上图所示。请求先经过LVS/HaProxy,到达基于OpenResty实现的API网关,API网关会根据请求将流量upstream到服务单元。服务单元作为一个整体,支持通过Lua、Node.js、Java等语言实现业务逻辑,启动时向Zookeeper注册,API网关会从Zookeeper获取服务单元部署信息。


服务单元如上图所示。它由Openresty统一对外暴露服务端,Openresty内置了lua语言,可以在weblua框架中通过编写lua程序来进行业务逻辑处理。Openresty通过proxy_pass,upstream将请求转给webnode处理,也可以在Openresty中通过配置处理Java业务逻辑。服务单元就像一个抽屉柜,具体的业务(app)就像一个抽屉,只要尺寸符合抽屉柜的要求,就能将抽屉推入到抽屉柜中,抽屉所用的语言不作要求。


Openresty集成了lua脚本作为编程语言,使用Zookeeper服务注册。为了解决lua与Zookeeper不适配问题,在Openresty启动时启动websocket服务,Node.js进程启动时同步启动websocket的client,这样每个Node.js进程会和Openresty保持长连接和心跳,Openresty会选择一个Node.js进程,启动Zookeeperclient完成服务注册。API网关也是基于服务单元通过类似的方式实现完成服务注册的。


服务单元在Openresty层完成了统一鉴权,不会产生额外的性能开销。

 

我们可以用“通道化”来阐述服务间的调用问题。我们将微服务之间的调用比喻成水管,从水管的一端流进去的是请求信息,那么从另一端流出来的也应该是请求信息,我们只要求流入流出的信息保持一致,而不关心这些信息在传输过程中经过了转换或其他过程。如上图,A、B之间的调用通过进程内require就能实现,而A、C间的调用是通过服务单元内的http完成的。

 

 

Q&A环节


Q:微服务架构在运维部署是否会很麻烦?


A:随着微服务的不断推进,服务数量势必会越来越多,这就需要考虑DEVOPS。比如代码提交之后通过Jenkins自动触发打包编译,自动生成版本号,进而触发自动测试部署和自动化测试,测试通过后进行一键部署升级、支持升级失败自动回滚等。我们目前已经实现了自动打包和测试部署,后续环节正在推进。


Q:Openresty里对Session管理进行了处理,开发人员会不会觉得不方便?


A:在引入微服务框架之前,公司有很多独立业务服务,每个服务都有自己的账号系

统实现登录验证逻辑。虽然有现成的代码模块可以用,但每次都需要重新测试验证。如今,具体的业务服务都可以通过Openresty完成鉴权并统一对外,对开发和测试人员来说,反而减少了一定的工作量。

 

以上内容是俞锋锋Qcon大会的《基于OpenResty和Node.js的微服务架构实践》演讲的主要内容,如有问题,欢迎与我们留言探讨。




  • 在线咨询
  • 技术咨询
  • 业务咨询
  • 电话咨询