基于微服务架构的预约摄影平台的设计与实现
2020-12-28李步官潘志宏张东源林劲文蔡杰远李赟
李步官 潘志宏 张东源 林劲文 蔡杰远 李赟
摘要:随着项目的功能需求的增加,传统的单体应用已经变得臃肿,难以继续进行开发维护,因此提出基于微服务架构的系统架构方法对单体应用进行改造。确定微服务架构之后,选用了Spring Cloud技术体系,并将系统拆分为多个微服务模块。在微服务的开发过程中,由于服务数量多,难以手动集成和手动部署每一个服务,因此结合DevOps,采用了阿里云云效平台的流水线功能实现持续集成、持续部署,同时将服务部署到Kubernetes和Docker中,实现了软件产品的持续交付,使得开发效率得到了极大的提升。
关键词:微服务;Spring Cloud;DevOps;Docker;预约;摄影
中图分类号:TP312 文献标识码:A
文章编号:1009-3044(2020)31-0026-05
Abstract: With the increasing functional requirements of the project, the traditional single application has become bloated and difficult to continue development and maintenance. Therefore, a system architecture method based on micro-service architecture is proposed to improve the single application. After determining the micro-service architecture, the Spring Cloud is selected and divided into several micro-service modules. During the development of micro-services, it is difficult to manually integrate and deploy each service because of the large number of services. Therefore, combined with DevOps, using the pipeline function of Ali Yunxiao Platform to achieve continuous integration and deployment. At the sametime, deploying services to Kubernetes and Docker to achieve continuous delivery of software products, which greatly improves development efficiency.
Key words: microservice; spring cloud; DevOps; docker; reservation; photography
1 背景
预约摄影平台的后端系统主要功能包括用户模块、约拍模块、作品模块和打卡点推荐模块。前期规划时系统采用Java语言开发,选用单体架构(如图1)进行模块化设计。由于整个系统构建在单体架构下, 单体应用的架构已无法满足业务的发展。在现有单体架构应用程序,如果代码有细微的改动,都要手工重新编译代码、打jar包、上传到服务器、重新部署应用,所谓的牵一发而动全身,这样烦琐的手工操作流程,使得整个系统部署上线,持续交付的耗时长,影响范围大、风险高、风险高,系统的开发效率低[1]。
鉴于以上所述的单体架构应用的缺点,提出了新的架构方案——微服务架构,来替换之前的单体架构应用。Spring Cloud微服务架构是目前应用最广的技术,为此选择Spring Cloud微服务架构的方案对系统进行改造,相比之前的单体架构的应用,新方案确实降低了开发难度,提升了开发效率,证实了该方案的可行性。
2 基于微服务的架构方案
微服务是系统架构上的一种设计风格,将一个原本独立的单体架构应用系统拆解为多个小型服务,即微服务,每个微服务都在各自独立的进程中运行,服务之间通过基于HTTP的RESTful API进行通信协作[2]。被拆分成的每一个服务都围绕着系统中的某一项或某一些耦合度较高的业务功能进行构建,并且每个服务都维护着自身的数据存储、业务开发、自动化测试案例以及独立部署机制[3]。微服务架构模式有非常明显的优势,特別是在实施敏捷开发和复杂的企业应用交付方面[4]。
结合预约摄影平台后端系统的业务情况,将单体应用拆分为微服务后,每个服务中的类遵守单一职责原则[5],将相同职责的类放到一起,不同职责的类分解到不同的接口和实现类中去。一个类的职责减少,代码少了,代码的复杂度就降低了,随之代码可读性也会提高,可维护性也会提高。根据现有后端系统的单体架构,将其改造为微服务架构的总体架构图2所示,服务拆分为用户服务、约拍服务、订单服务、作品服务、打卡点服务、图片服务、轮播图服务、搜索服务等。
3 系统的设计与实现
3.1 架构搭建
3.1.1 基本的开发环境软件版本
3.1.2 搭建统一的项目依赖
为了方便管理各个服务的依赖,需要建立一个统一的项目依赖。将该项目依赖作为所有工程的父依赖,在pom.xml文件中指定properties属性,即可统一管理所有项目的依赖版本。
3.1.3 搭建分布式配置中心
分布式配置中心采用Spring Cloud Config,该微服务组件是为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,由客户端和服务端两部分组成。其服务端是一个独立的微服务应用,用来连接配置仓库,并为客户端提供获取配置信息,加密/解密信息的访问接口。其客户端则是各个微服务应用,每个微服务应用在配置文件中指定配置中心服务端的地址,在应用启动的时候会自动地去指定的配置中心服务端地址中加载相应的配置信息。分布式配置中心搭建步骤如下:
1)服务端的配置
添加Spring Cloud Config项目依赖,新建ConfigApplication启动类,并在该类中添加@EnableConfigServer注解,表示启用配置服务器,将该应用作为配置中心服务器。在代码托管平台中创建一个仓库,用来存放各个微服务应用的配置文件。yml配置信息如下:
spring:
cloud:
config:
label: master
server:
git:
uri: https://gitee.com/username/repo.git
search-paths: repo
username:
password:
2)客户端的配置
以服务注册中心panda-eureka为例,在pom.xml中添加spring-cloud-starter-config依赖,并且在yml配置文件中添加如下配置信息:
spring:
cloud:
config:
uri: http://localhost:8888
3.1.4 搭建服务注册与发现中心
服务注册中心(service registry)是服务发现的一个关键部分。它是一个包含了服务实例网络位置的数据库。服务注册中心必须是高可用和最新的。虽然客户端可以缓存从服务注册中心获得的网络位置,但该信息最终会过期,客户端将无法发现服务实例。因此,服务注册中心由使用了复制协议(replication protocol)来维护一致性的服务器集群组成[4]。
Netflix Eureka 是一个很好的服务注册中心微服务组件。它提供了一个用于注册和查询服务实例的 REST API。服务实例使用 POST 请求注册其网络位置。它必须每隔30秒使用PUT请求来刷新其注册信息。通过使用 HTTP DELETE 请求或实例注册超时来移除注册信息。客户端可以使用HTTP GET 请求来检索已注册的服务实例。Eureka也有服务端和客户端组成,服务端主要处理服务注册,客户端处理服务的注册与发现。搭建步骤如下:
1)Eureka服务端
在Eureka项目中添加spring-cloud-starter-netflix-eureka-server依赖,yml配置文件信息如下:
spring:
cloud:
config:
uri: http://localhost:8888
label: master
name: panda-eureka
profile: dev
2)客户端的配置:
以用户服务panda-service-provider-user为例,在项目的pom.xml文件中添加spring-cloud-netflix-eureka-client依赖,yml文件配置如下:
spring:
cloud:
config:
uri: http://localhost:8888
label: master
name: panda-service-provider-user
3)Eureka工作效果
3.1.5 搭建API网关
API网关是一个智能的应用服务器,所有的外部客户端访问都需要经过它来进行调度和过滤。在该项目的预约拍照系统中,采用Spring Cloud Zuul作为API网关,并将Spring Cloud Zuul与Spring Cloud EurekaI进行整合,将Zuul注册为Eureka服务治理下的应用,同时从Eureka中获得了所有其他微服务的实例信息,将维护实例的工作交给了服务治理框架自动完成,不需要人工介入。
1)构建网关
新建Zuul的Spring Boot项目,添加相关依赖,在启动类上添加@EnableZuulProxy开启API网关服务功能。
2)配置請求路由
路由功能与Eureka结合使用,在Eureka的服务注册与发现体系中,每个服务既是服务提供者,也是服务消费者,所以作为消费者的微服务应用也可以作为服务提供者向路由网关提供服务。
下面以用户服务提供者为例,为panda-service-provider-user配置路由。
zuul:
routes:
v1-provider-user:
path: /api/v1/user/**
上面的配置中,在zuul的routes节点下配置了一个路径为/api/v1/user/的路由来映射用户服务提供者的接口地址。路由搭建好后,通过访问API网关配置的路由地址可访问到用户服务提供者的接口地址。可以看到,上面这种简单的path加serviceId的映射组合,称之为面向服务的路由配置,因为Zuul与Eureka结合使用,Zuul只需要指定serviceId,路由转发时可以从Eureka中获取到服务实例的地址(ip和端口),这种配置方式使得API网关服务可以自动化完成服务实例清单的维护,完美地解决了对路由映射实例的维护问题。
3.1.6 搭建链路追踪系统
随着预约拍照系统的开发,微服务应用越来越多,系统规模越来越大,各微服务间的调用关系变得错综复杂,几乎每一个前端请求都会形成一条复杂调用的链路。为解决以上问题,采用ZipKin作为服务链路追踪系统。
1)搭建ZipKin Server
搭建Spring Boot项目,添加相关依赖zipkin,zipkin-server,zipkin-autoconfigure-ui。在启动类上添加@EnableZipkinServer注解。然后在yml配置文件中添加如下配置即可。
spring:
application:
name: panda-zipkin
main:
allow-bean-definition-overriding: true
server:
port: 9411
2)在微服务中添加ZipKin客户端配置
以用户服务提供者为例,在pom.xml文件中添加spring-cloud-starter-zipkin依赖,在yml配置文件中添加ZipKin Server的url配置。
spring:
zipkin:
base-url: http://localhost:9411
3.2 核心业务模块
预约摄影服务是整个系统的核心业务,为模特和摄影师提供预约的服务,核心业务代码如下。
3.2.1 发布预约
public int addAppointment(TAppointment appointment){
//传递到Dao层,并保存数据到数据库
int i = appointmentMapper.addAppointment(appointment);
if (i != 0){
appointmentRepository.save(appointment);
}
return i;
}
上面的代码比较简单,该代码位于Service层,作用是将从Controller层传过来的数据传到Dao层,并将数据保存到数据库。
3.2.2 预约下单
public int addOrder(TOrder order) {
int i = orderMapper.addOrder(order);
//成功将订单插入数据库,将消息发送到MQ
if (i != 0) {
int id = order.getId();
TOrder.MyOrder myOrder = orderMapper.getOrderAndUserAndAppointmentById(id);
//获取约拍时间
String startDatetime = myOrder.getStartDatetime();
//约拍时间不为空的情况下执行
if (!StringUtils.isEmpty(startDatetime)) {
int hour = 2;
//提前发送邮件提醒
Long stringDateToTimestamp = DateUtils.getStringDateToTimestamp(startDatetime);
Long remindDateTimestamp = (stringDateToTimestamp - 1000 * 60 * 60 * hour) / 1000L;
String remindDate = DateUtils.getTimestampToStringDate(remindDateTimestamp);
//短信通知
SMSNoticeJobDTO smsNoticeJobDTO = new SMSNoticeJobDTO(
myOrder.getUsername(),
myOrder.getTitle(),
"" + hour,
remindDate,
myOrder.getPhone());
String smsJsonString = JSONObject.toJSONString(smsNoticeJobDTO);
mqService.sendMessage("exchange.direct", "panda.sms", smsJsonString);
//邮件通知
String emailText = "亲爱的" + myOrder.getUsername()
+ ",您预约的标题为【" + myOrder.getTitle()
+ "】" + ",还有" + hour
+ "个小时就要开始啦,请做好准备。祝您生活愉快!\n【熊猫约拍】";
EmailJobDTO emailJobDTO = new EmailJobDTO(
"job_" + myOrder.getOrderNo(),
remindDate,
myOrder.getEmail(),
"約拍时间提醒",
emailText);
String emailJsonString = JSONObject.toJSONString(emailJobDTO);
mqService.sendMessage("exchange.direct", "panda.email", emailJsonString);
}
}
return i;
}
这段代码是用户添加预约摄影的代码,该代码的逻辑是如果成功将订单插入数据库,将消息发送到消息队列MQ,由MQ处理发送邮件和短信提醒的异步任务。
3.3 前端的实现
前端分为网站和移动客户端,网站选用了目前最火的前端框架Vue.js。Vue.js是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合[6]。在移动端上,选用了uni-app。uni-app 是一个使用 Vue.js开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、H5、以及各种小程序[7]。使用这两个框架,可以快速的搭建出网站应用和移动客户端应用。
3.3.1 网站前端的实现
addOrder(pubUid, acptUid, aptId, image = '') {
if (this.isLogin){
let param = {
pubUid: pubUid,
acptUid: acptUid,
aptId: aptId,
image: image
};
request({
url: ORDER_ADD,
method: 'post',
data: this.qsParam(param),
}).then(res => {
if (res.data.code === 1) {
this.$message.success("预约成功");
this.addedOrder = true;
}
}).catch(err => {
this.$message.error("预约失败, 请稍候重试");
})
}else {
this.$message.warning("请先登录");
}
}
这段代码是前端处理添加预约摄影的逻辑代码。通过发送网络请求向后端发送数据。
网站UI的设计方面,采用饿了么公司开源的UI框架Element-UI,它提供大量常用的UI组件,结合上述三个框架,完成网站前端的设计,网站主页效果图如图4所示。
3.3.2 移动客户端的实现
移动客户端采用uni-app,移动端跨平台的框架。相比Vue.js框架,uni-app在vue.js的基础之上,将常用的功能和组件进行了封装,如网络请求、UI组件等,方便开发者快速进行开发。移动客户端的效果图如图5所示,以uni-app打包的Andriod APP为例。
4 结合DevOps实现持续集成、持续部署
DevOps是一套实践方法,在保证高质量的前提下缩短系统变更从提交到部署至生产环境的时间。DevOps的重点是通过发展创建一个稳定、快速的开发工作流和IT运维。DevOps一方面可以缩短系统的部署时间,另一方面可以减少系统的缺陷,大大提高系统的质量[8]。
4.1 DevOps平台的选择
在IT技术快速发展的今天,已经涌现出了大批的DevOps平台,前有老牌的持续集成开源项目软件Jenkins,后有国内一流的互联网公司推出的DevOps平台,如阿里云的云效,腾讯的蓝鲸智云,百度的效率云等等。预约拍照系统选择了阿里云云效平台,由于服务器是在阿里云平台购买的云服务器ECS,结合阿里云云效平台,可将项目快速地通过云效平台部署。
4.2 阿里云云效平台的使用
4.2.1 创建项目和应用
创建项目,项目为必创建项,方便管理项目下的每个应用。每一个微服务都要创建一个对应的应用。
4.2.2 创建镜像仓库
在阿里云镜像服务中为应用创建一个专门的镜像仓库,用来存放流水线完成的制品。
4.2.3 创建并配置流水线
进入相应的应用,并创建流水线,填写代码仓库、分支、开启webhook,并将地址添加到代码仓库,配置环境,填写部署脚本和执行用户,保存配置即可。
4.2.4 运行流水线
因为创建流水线时配置了webhook,所以只要提交代码到仓库,即可触发webhook,使流水线自动运行,实现持续集成、持续部署。流水线运行图如图6所示。
5 结束语
随着互联网的技术的发展,近年来微服务在应用开发和部署方面取得了显著的进步。相比采用传统单体应用架构的系统,预约拍照系统后端服务结合微服务架构、DevOps文化、持续集成与持续部署(CI/CD)和容器引擎技术,将服务部署到云端,极大地提高了系统的开发效率和维护效率。解决了单体应用架构代码臃肿、开发效率低、软件交付时间长等问题。
微服务架构在软件开发上主要有两种应用模式,其中一种是从需求分析出发,从无到有地开发一个新的微服务应用程序;另一种应用是将已有系统(通常是单体应用程序)重构到微服务架构[9]。目前业务还未划分完整,下一阶段就是需要结合业务场景和服务器资源进行细度的划分预约拍照系统的后端服务,在有限的服务器资源下合理地划分服务,最大化地利用服务器资源完成业务功能,使得系统能够更加高效稳定地运行。
参考文献:
[1] 刘从军,刘毅.基于微服务的维修资金管理系统[J].计算机系统应用,2019,28(4):52-60.
[2] FOWLER M.Microservice[EB/OL].(2014-04-25)[2020-04-18].http://martinfowler.com/articles/microservices.html.
[3] 翟永超.Spring Cloud微服务实战[M].北京:电子工业出版社,2017.
[4] Richardson C,Smith F.微服务:从设计到部署[EB/OL].Oopsguy,译. [2020-04-18].https://github.com/DocsHome/microservices .
[5] 高松,牛治永.敏捷设计原则与设计模式的编程实践——单一职责原则与依赖倒置原则[J].计算机应用,2011,31(S2):149-152.
[6] 高棟,王殿胜,张思琪,等.DevOps平台建设分析[J].中国科技信息,2019(24):39-40.
[7] vue.js中文官网[EB/OL].[2020-04-18]. https://cn.vuejs.org/.
[8] uni-app官网[EB/OL].[2020-04-18].https://uniapp.dcloud.io/.
[9] 吴化尧,邓文俊.面向微服务软件开发方法研究进展[J].计算机研究与发展,2020,57(3):525-541.
【通联编辑:谢媛媛】