基于微服务架构的众筹系统
2020-01-07吴侠艾芳菊
吴侠,艾芳菊
(湖北大学计算机与信息工程学院,湖北 武汉 430062)
0 引言
图1 单体架
众筹系统是用来管理众筹网的核心业务平台,其主要功能包括基础模块、会员模块和项目模块.由于目前传统的众筹系统都采用单体架构进行模块化设计、采用统一的编程语言进行开发、打包及部署,具体如图1所示.与此同时,单体架构的缺点也逐渐显现出来.
首先,随着业务的不断发展,整个系统的复杂度和更新频率不断提高,面对这种情况,传统的单体架构已经无法满足业务发展的需要.在保证当前系统能够稳定运行的前提下,如果对单体架构应用程序做任何细微的修改,那么对于整个应用来说就必须进行重新部署,这样会极大降低系统的构建效率[1].其次,由于系统采用单体架构,采用新框架或者新技术需要重新编写程序,会极大增加开发成本.与此同时,系统部署环境的配置无法根据业务模块进行动态伸缩,只能对各个模块进行统一配置,无法为每个模块设置最优的配置.最后,由于每次升级系统或者缺陷修复都需要对整个WAR包进行重新部署,造成整个系统全量部署耗时长、影响范围大、风险高[2].
因此,笔者提出了基于微服务构建众筹系统的技术方案,并针对在构建系统时出现的问题如:服务治理、配置集中管理、路由网关、服务链路追踪等,采用Spring Cloud的一系列组件作为最佳解决方案,在众筹系统搭建的过程中逐渐证明了其可行性.
1 基于微服务架构的系统实现
1.1 使用微服务架构搭建系统将整个众筹系统按照业务划分成3个模块分别是基础模块、会员模块、项目模块.模块之间通过HTTP协议或者轻量级的消息组件进行通信.这些微服务单元是高度组件化的模块,模块之间拥有稳定的边界,模块之间没有任何耦合,有非常好的扩展性和复用性[3].每个模块都可以拥有自己的编程语言和存储技术,它们之间相互独立不受影响.
按照业务将系统划分为多个服务单元,能更好地进行开发和维护,主要因为划分之后每个服务的代码量小,业务清晰可见,每个服务都可以选择适合自己业务的技术.同时能够对每个服务进行差异化的配置如:为不同的服务单独增加内存、提高CPU或者其他配置来满足业务需求.
同时为了提高代码的可读性、可扩展性和可维护性[4],将每个模块的前后端分离,实现更细粒度的划分.使得前端微服务注重于向用户展示数据,后端微服务注重于数据获取.两者分离各司其职,使系统更加高效稳定的运行.
1.2 选择Spring Cloud实现整体架构Spring Framework为 Java 企业级开发提供了一站式的轻量级解决方案,目前已经成为了Java企业级领域事实上的标准[5].Spring Cloud作为Spring Framework的一部分,是一个基于Spring Boot实现的微服务开发架构,它的子项目涵盖了所有分布式系统所需要的基础设施,而且作为 Spring的项目,能够与 Spring Boot、Spring Data等其他Spring项目完美融合,极大减少了已有项目迁移成本[6],也能结合企业信息技术进行的实际情况,及时跟随开源社区组件的最新动态和技术优化,引入最稳定的前沿技术对已有技术进行不断改造升级[7].
它为服务治理、配置集中管理、路由网关、服务链路追踪等提供了一种相对简单的开发方式,而Spring Cloud是基于Spring Boot的,这样就可以避免繁杂的配置和依赖管理,简化了开发和部署的过程.同时由于Spring Cloud来自于Spring Resouces社区,经过社区大量的兼容性测试,使其稳定性不断提高.
基于Spring Cloud框架的系统架构如图2所示.
图2 Spring Cloud架
在整个Spring Cloud框架体系中,根据不同的功能需要,选择相应的组件来支持系统的微服务,在实际开发过程中极大降低了沟通成本,极大提高了研发和测试效率.主要组件如下:
Eureka:服务注册与发现组件,包含服务注册中心、服务提供者和服务消费者[8].
Zuul:路由网关,主要功能是路由转发和过滤器.
Feign:声明式、模板化的HTTP客户端[9].
Ribbon:客户端负载均衡.
Hystrix:在SOA/微服务架构中提供服务隔离、熔断、降级机制的工具/框架[10].
Spring Cloud Config:分布式配置中心,它支持配置文件放在远程Git仓库中,同时支持客户端配置信息的刷新、加密/解密等.
Sleuth:在分布式系统中提供服务链路追踪的解决方案.
Zipkin:分布式实时数据追踪系统,聚集来自各个异构系统的实时监控数据[11].
由于整个系统是基于Spring Cloud来实现的,严格按照微服务的设计原则,服务之间的调用采用Feign.服务之间相互独立无耦合,具有快速交付和弹性伸缩的能力[12].
另外需要注意的是针对不同版本的Spring Cloud,具体的依赖和配置文件会有较大差别,本系统统一采用Dalston.RELEASE版本的Spring Cloud.
2 具体实现
采用上述架构方案实现众筹系统,主要实现环节和配置介绍如下.
2.1 服务注册与发现中心(Eureka)的应用在实际系统中往往要对Eureka Server进行高可用配置,以防止系统故障导致服务注册中心不可用,从而导致整个系统不可用.不同的服务注册中心的信息是共享的,无论在哪个中心注册都会及时同步到其他注册中心.另外,选用yml文件来维护系统的配置文件,因为yml文件层次清晰,易于编写和浏览.
此外,需要在服务注册与发现服务的启动类上添加@EnableEurekaServer注解开启Eureka Server的功能;在所有的服务消费者的启动类上添加@EnableEurekaClient注解开启Eureka Client的功能.
以会员模块为例,相关服务启动后会自动注册到服务中心,如图3所示.每个微服务都对应其特有的功能,各个微服务的业务说明如表1.
图3 微服务列
表1 微服务业务说明
2.2 配置的集中管理Spring Cloud Config默认将配置信息存储到远程的Git仓库.
2.2.1 服务端(ConfigServer)配置 以众筹项目系统后台部分crowdfunding-project-service为例,在生产环境中将ConfigServer注册到eureka-server,其他客户端即可通过微服务的服务名从Git仓库中远程获取配置信息.具体步骤如下:
首先,在服务端增加Eureka的依赖spring-cloud-starter-eureka.
其次,在yml文件中设置连接Git仓库的参数.
最后,需要在ConfigServer的启动类上添加@EnableConfigServer注解,开启ConfigServer的功能.
2.2.2 客户端(ConfigClient)配置 首先,在客户端增加Eureka的依赖spring-cloud-starter-eureka.
其次,客户端需要在yml文件中做相关配置即可.
2.3 路由网关(Zuul)的应用在Zuul的启动类上添加@EnableEurekaClient注解即可实现Zuul向Eureka进行注册,由于Zuul向Eureka中进行了注册,所以API网关可以被看作是Eureka治理下的一个微服务应用,它可以从注册中心中获取所有服务以及它们的实例信息.与此同时通过这些实例信息和API网关的配置,维护了系统中所有serviceId和实例地址的一对一的映射关系.当外部请求到达API网关时,根据请求URL找到对应的path,此时API网关就知道将请求转发到哪个具体的serviceId上.
如果某个服务存在多个实例,则Zuul默认在路由转发做了负载均衡,负载均衡策略为轮询,即将请求依次转发到不同的服务实例.
2.3.1 路由配置 以实名认证功能后台部分为例,为实名认证后台服务crowdfunding-authname-service配置路由,通过zuul.routes.< route>.path和zuul.routes.< route>.serviceId这两个参数对服务进行配置,其中< route>表示根据服务自定义的名称,例如:
zuul.routes.crowdfunding-authname-service.path=/crowdfunding-authname-service/**
zuul.routes.crowdfunding-authname-service.serviceId=crowdfunding-authname-service
通过这两个配置就可以将所有符合/crowdfunding-authname-service/**规则的请求转发到crowdfunding-authname-service所对应的服务实例上去.
2.3.2 熔断器配置 在Zuul中实现熔断器的功能需要实现ZuulFallbackProvider接口,这个接口有两个重要的方法,一个是getRoute()方法,主要用于指定熔断器的功能应用于哪些路由的服务;另一个方法是fallbackResponse(),它主要是进入熔断功能时执行的逻辑[13].
2.3.3 请求过滤 继承ZuulFilter这个类并且实现这个类中filterType()和filterOrder(),以及IZuulFilter的shouldFilter()和Object run()这4个抽象方法即可完成对请求的过滤和拦截.
在系统中自定义了一个Zuul过滤器CrowdfundingFilter,它继承了ZuulFilter并实现了ZuulFilter的4个方法.同时还要在启动类中为其创建具体的Bean之后才能够启动该过滤器.
2.4 服务之间的调用(Feign)Feign自身就是一个声明式的伪HTTP客户端,写起来思路更加清晰和方便,同时Feign是采用基于接口注解的编程方式,使用更加简便.以众筹项目系统前台部分crowdfunding-project为例,具体使用如下:
首先,为crowdfunding-project添加依赖spring-cloud-starter-feign.
其次,在crowdfunding-project 的yml文件中添加相关配置.
接着,在crowdfunding-project的启动类上添加@EnableFeignClients注解开启Feign的远程调用功能.
最后,编写接口类实现远程调用其他服务.
2.5 服务链路追踪与分析(Sleuth和Zipkin)在微服务架构下的众筹系统中一个服务功能的实现往往需要依赖其他服务,多个服务相互协调共同运行,最后才能产生请求结果.以实名认证前台部分crowdfunding-authname为例,它需要调用实名认证后台部分crowdfunding-authname-service.
2.5.1 Zipkin Server配置 首先,添加相关依赖spring-cloud-sleuth-zipkin-stream、spring-cloud-starter-stream-rabbit、zipkin-autoconfigure-ui、mysql-connector-java、spring-boot-starter-jdbc.
其次,在启动类上添加@EnableZipkinStreamServer注解开启Zipkin Server功能.
最后,在Zipkin Server的yml文件中添加相关配置.
2.5.2 Zipkin Client配置 Zipkin Client包含两个部分,分别是crowdfunding-authname和crowdfunding-authname-service.
首先,为两个服务添加依赖spring-cloud-sleuth-zipkin-stream和spring-cloud-starter-stream-rabbit.
其次,以crowdfunding-authname为例,在其yml文件中添加相关配置即可.
通过访问Zipkin Server可以看到具体的访问过程,服务之间的相互依赖关系如图4所示.服务请求的调用情况如图5所示,从图中可知请求的调用情况,如请求的调用时间、消耗时间、以及请求调用的链路情况.
图4 服务之间的依赖关
图5 请求调用情
3 系统性能分析
本系统采用ApacheBench作为压力测试工具.以同一个请求为例,设置发起的总请求数为100、并发数也为100.从整体性能上看基于微服务架构的系统从整个测试持续的时间、每秒请求的数量、每个请求消耗的时间和平均每秒网络传输速率都比基于单体架构的系统有较大提升.具体数据如表2所示.
表2 两种架构下系统运行数据
图6 两种架构内存消耗
众筹系统部分功能在高并发的情况下两种架构的内存消耗如图6所示.
两者相比不仅仅是系统性能上的提升,同时基于微服务架构的系统还具有更好的可维护性、可扩展性,代码的可读性也极大提升;业务交接的难度也降低了很多;测试难度也极大地降低.小组中的每个人只需要关注自己的微服务即可,无论其他微服务做了哪些修改,都不会影响自身的微服务,极大提升了开发效率.
4 总结
随着互联网技术的飞速发展,传统的单体架构已经很难满足互联网技术的发展要求,微服务架构应运而生.相比于单体架构,微服务架构能够将复杂问题简单化,具有很强的横向扩展能力,服务之间相互独立,无耦合,更重要的是微服务在CAP架构中采用的是AP架构即高可用和分区容错,因此微服务架构具有很好的负载能力,同时系统也很健壮[13].
通过对传统众筹系统的改造,使得新系统的维护效率大大提高,应用发布和更新的时间也被极大地缩短,因此从长远来看,该系统具有很好的推广价值和应用场景.
另外,微服务架构是系统架构不断演进的一个产物,任何软件从一开始就不应该被设计成微服务架构,而是需要根据具体的业务渐进式的发展与改进,任何脱离实际业务的技术都是无用的.目前整个系统还未实现真正意义上的微服务,有些微服务并不是严格按照业务来划分.下一阶段就是需要结合具体的生产数据以及链路追踪收集的数据对系统进行更进一步的调整包括将服务继续划分以及更改服务的配置,使得系统能够更加高效稳定的运行.