在今年的LC3大会上,ServiceComb展台所展示的demo视频“30分钟开发雏形CRM应用”引起了参会者的广泛关注,大家纷纷对其背后的技术表现出浓厚的兴趣。本文将从房地产企业的客户管理管理场景入手,使用领域驱动设计,深入技术细节,详解如何快速开发落地一个微服务化的客户管理系统。

牛刀小试

打开浏览器,输入地址http://start.servicecomb.io/打开SERVICECOMB SPRING INITIALIZR,修改Project Metadata中的Group,Artifact和ServiceComb Parameters中的ServiceCenter Address,Governance等,点击GenerateProject,解压生成下载的demo.zip。

运行它也很简单,使用IDE打开项目,DEBUG -> Application.java,或在命令行:

稍等微服务启动就绪,打开浏览器输入http://localhost:9080/hello验证一下:

是不是非常轻松呢?

脚手架

在建筑领域,脚手架是施工现场为方便工人操作并解决垂直和水平运输而搭设的各种支架以及平台。

在软件开发领域,它引申为预提供一些基础框架代码加速开发过程,避免从零开始构建项目。用户只需要依据需求场景选择合适的脚手架,然后填充定制的业务逻辑即可,不必再去处理一些基础功能,例如数据库连接、日志实现、RPC传输等。

微服务框架一般都会提供脚手架功能,例如Spring,提供了SPRING INITIALIZR;ServiceComb基于SPRING INITIALIZR,提供了更具优势的特性:

生成的项目除了在POM中自动添加必要的依赖,还会提供Producer和Consumer示例代码(Hello World);会进一步提供Edge Server、Authcation Server等更贴近业务的脚手架项目,让用户能快速构建体系完整的微服务系统。

那么什么叫一个完整的微服务系统呢?我们可以拿一个具体的场景做例子,会更有感觉:

场景:地产CRM

您经营着一家房地产开发商,销售房产,迫切需要一套销售系统,考虑到微服务的优势,您决定使用微服务的方式构建系统;主要的业务流程也非常简单:用户前来购买购买产品(房产),首先需要登记用户信息,并缴纳一定数量的定金,待交易当日,挑选心仪的产品(房产),支付尾款,完成交易。

1、使用DDD指导地产CRM系统的设计

微服务系统的设计方面,领域驱动设计(Domain-Driven Design,DDD)是一个从业务出发的好选择,它由Eric Evans提出,是一种全新的系统设计和建模方法,这里的模型指的就是领域模型(DomainModel)。领域模型通过聚合(Aggregate)组织在一起,聚合间有明显的业务边界,这些边界将领域划分为一个个限界上下文(Bounded Context)。Martin Fowler对它们都有详细的解读 [1]。

理论概念都搞清楚了,那么怎么来找模型和聚合呢?一个非常流行的方法就是Event Storming [2],它是由AlbertoBrandolini发明,经历了DDD社区和很多团队的实践,也是一种非常有参与感的团队活动:

上图就是我们对地产CRM这个场景使用Event Storming探索的结果,现在我们能够将限界上下文清晰的梳理出来:

提示:Event Storming是一项非常有创造性的活动,也是一个持续讨论和反复改进的过程,不同的团队关注的核心域(Core Domain)不同,得到的最终结果也会有差异。我们的目的是为了演示完整的微服务系统构建的过程,并不涉及商业核心竞争力方面的探讨,因此没有Core Domain和Sub Domain之类的偏重。

2、将分析成果转化为方案域设计

当我们完成所有的限界上下文的识别后,可以直接将它们落地为微服务:

用户服务:提供用户信息管理服务,这里保存这用户的账号和密码,负责登录和认证;产品(房产)服务:提供产品管理服务,保存着房产的信息诸如价格、是否已售出等信息;支付服务:提供交易时支付服务,模拟对接银行支付定金,以及购房时支付尾款;

由于完成一笔交易是一个复杂的流程,与这三个微服务都有关联,因此我们引入了一个复合服务——交易服务:

交易服务:提供产品交易服务。它通过编排调用将整个交易流程串起来,交易服务中有两个流程:定金支付

Step1:通过用户服务验证用户身份;

Step2:通过支付服务请求银行扣款,增加定金账号内的定金;

购房交易

Step1:通过用户服务验证用户身份;

Step2:通过资源服务确定用户希望购买的资源(房产)尚未售出;

Step3:通过资源服务标记目标资源(房产)已售出;

Step4:通过支付服务请求扣减定金账号内的定金,以及银行扣剩下的尾款;

最后两个步骤需要保证事务一致性,其中Step4包含两个扣款操作。

之后,我们引入Edge服务提供统一入口:

Edge服务:很多时候也被称为API网关(API Gateway),负责集中认证、动态路由等等;

提示:Edge服务需要依赖服务注册-发现机制,因此同时导入了ServiceCenter。

最后还需要提供UI:

前端UI(同样以微服务方式提供):用户交互界面;

至此,DDD设计地产CRM的工作就结束了。

快速实现客户关系管理系统的用户服务

1、用户微服务并不简单

用户微服务是所有系统中不可或缺的部分,它承载了认证和授权等核心功能——无论是登录一个网站、还是打开一个APP,当涉及到需要身份识别后才能够执行的操作,都需要用户微服务把关。例如观看视频网站上的视频,匿名用户会插播广告,如果希望屏蔽广告,则需要登录并购买VIP会员,登录即是身份认证的过程,而VIP屏蔽广告即是授权的过程。

认证

认证不仅仅是一次性验证用户名和密码的过程,还需要能反复使用认证的结果,确保后继所有操作都是合法的,这就涉及到“有状态”,但HTTP是一个无状态协议,如何能够将登录成功后的认证信息与后继的请求关联起来呢?

我们非常熟悉的做法是使用Session或Cookie:

Session存储在服务端,因此具备良好的防篡改能力,但弊端是使服务有状态,微服务系统中,同一个微服务会依据系统压力的大小弹性伸缩出多个运行实例负载均衡,跨实例访问会状态丢失。Cookie存储在客户端,它正好与Session相反,优势是服务不必保持状态,但弊端是客户比较容易的篡改Cookie信息,例如修改过期时间以逃避验证,而且浏览器对Cookie也有较多限制。

那么,如何兼顾这两方面的需求呢?Token就是一个比较好的解决方案。

Token中文翻译为令牌,它将登录认证后的信息签名后返回,服务端不保存,客户端请求的时候将认证的完整信息附带上提供给服务端验签,签名可以保证信息不被篡改。了解了了解Token的原理,自然要关注Token的格式,JWT就是这样一个基于JSON的开放标准RFC-7519 [3]。

JWT (Java Web Token)规范

简而言之JWT规范由三部分构成:

1、Header: 声明Token的类型也就是JWT,以及加密算法,例如:

{

"typ":"JWT",

"alg":"HS256"

}

2、Playload:存放有效信息,既包含标准签发者、用户、签发时间、过期时间,唯一标识等信息;也可以存放用户自定义的声明信息,例如权限控制相关的内容,例如:

{

"sub":"1234567890",

"name":"YangYongZheng",

"iat":1516239022

}

3、Signature:签名信息,包含Header和Playload的原始信息(Base64编码过)以及签名过后的信息。

提示:JWT IO提供了在线编码和解码工具 [4]。

授权

授权的本意是指将完成某项工作所必须的权力授给下属人员,在软件系统中往往引申为使人或角色具备访问特定资源或更改行为的许可。例如之前提到的VIP屏蔽广告,即是视频网站允许播放终端在特定的帐号登录后跳过广告播放环节(行为)的许可。

授权系统比较常见的做法有ACL和RBAC:

ACL:ACL全称Access Control List,它是以受控资源为核心,每一个受控资源,都有一个权限控制列表记录哪些用户或角色对这项资源执行具体操作(也被称为授权点)的权限设置,例如查询(可见)、修改、删除等等。Windows中的文件系统安全即是一个经典的ACL实现案例:

RBAC:RBAC全称Role Based Access Control,与ACL相比,它以角色为核心,权限落地在角色上,不为特定用户授权。它的优势是大幅简化了用户与权限的管理,在受控对象不多或控制粒度要求不高(例如接口访问控制)的场景下非常适用。

由于微服务系统的权限控制主要是接口访问控制上,并且多采用用户组方式组织用户,因此RBAC是比较流行的做法。

2、实现用户微服务

第一步:创建微服务项目

使用SERVICECOMBSPRING INITIALIZR创建用户微服务,创建完毕后使用IDEA或Eclipse打开项目,我们删掉HelloImpl和HelloConsumer,之后添加自己的实现。

第二步:使用MySQL持久化用户信息

用户微服务需要持久化用户信息,我们使用MySQL数据库,ORM使用SpringData JPA:

引入依赖

查看原文 >>
相关文章