CloudWeGo Study Group 是由 CloudWeGo 社区发起的学习小组,开展以 30 天为一期的源码解读和学习活动,帮助新成员融入社区圈子,和社区 Committer 互动交流,并学习上手 CloudWeGo 几大框架项目。目前 CSG 第四期—— CloudWeGo 业务实践案例解读已经圆满结束!
本期活动期间安排了 4 期直播分享,主题分别为:
- Bookinfo——基于 CloudWeGo 重写 Istio 经典 demo
- EasyNote——CloudWeGo 生态入门
- Open Payment Platform——基于 CloudWeGo 实现 API Gateway
- BookShop——从上手电商到上手CloudWeGo
本文为 CSG 第四期第二场直播中CloudWeGo Reviewer 王伟超分享内容。
一、嘉宾介绍
利用 Kitex 和 Hertz 进行支付开放平台的开发演示,使用 CloudWeGo 实现 API Gateway ,参考最佳实践,力求简洁、清晰得展示效果。
本期分享的内容主要分为以下三个部分:
- 背景介绍
- 架构设计
- 工程实践
- 总结
二、背景介绍
这期主题是关于 Open Payment Platform,即支付开放平台业务,基于 CloudWeGo 来实现 API Gateway。
今天的分享会分 4 个部分,
- 第一部分是编写 biz-demo 的背景;
- 第二部分从整体架构上来讲是如何设计的;
- 第三部分分享具体的实践步骤;
- 第四部分对本次分享做的小结。
项目背景
首先,在做这个 biz-demo 之前我一直有个疑问。这个疑问是从去年 3 月份开始,当时刚接触到 Kitex 不久,了解到 Kitex 有泛化调用的特性,但是并不是很清楚这个特性有什么用、用在什么场景。 另外,我们都知道微服务一般不直接在公网暴露,那么公网或者用户普通的 HTTP 请求是怎么到 RPC 请求的呢?这个处理链路当时不是很清晰。
于是,我在 Github 提了一个讨论:“关于泛化调用及如何从集群外访问具体的某服务”,后来也是很快得到社区小伙伴的反馈和解答。不过当时对此认识不是特别深,后续也是带着这个问题一直在了解这方面的知识。
直到去年的 9 月份,我们社区的广明提到想基于泛化调用做一个 biz-demo,我觉得这是一个很好的尝试,想借助这次机会很好的了解该特性。一边学习一边做输出,这是很好的学习方式,所以就有了后续的这个 demo。
支付开放平台
支付开放平台通常是开放给服务商或商户提供收款记账等能力的服务入口,常见于支付宝、微信、银联等第三方或第四方支付渠道商,特别是前几年发展起来的聚合支付方向。
我做这个 demo 也是因为曾经做过第四方的支付,有接过很多的支付 SDK,所以稍会稍微熟悉它的业务场景特点,详细分析如下。
- 第一,它对外暴露的是 HTTP 接口,结合我们社区已有的项目,可以用 Hertz 来做另外一个开放平台。
- 第二,需要有加签、验签的功能,正好可以用 Hertz 的 Middleware 去自定义它的加签验签逻辑。
- 第三,也是特别重要的一点,即业务场景、业务模块很清晰,比如商户、支付、对账相关的安全边界很详细,也非常适合我们做业务、做微服务的拆分。
- 最后,关注工程化,比如 ORM、分包、代码分层、错误统一定义及优雅处理。比如分包在之前可能会比较简单、随意,这次也想能更多借鉴社区或者业内稍微能达成共识的设计。
三、架构设计
整体架构
首先从整体的角度去看这个项目,如图所示:
1、左边用户发起了 HTTP 请求,POST 方法,指定特定的服务路径参数,传了某些业务参数到了网关;
2、网关会判断它是要去到哪一个 RPC 微服务,然后用传递过来的参数,由泛化调用客户端,向 RPC 服务发起请求。
在此之前,启动 hert-gateway 时,还有一个前置处理:解析了所有的 IDL 文件,生成了 RPC 服务 -> 泛化调用客户端
的映射关系,后续每个 RPC 服务都使用各自的一个客户端发起泛化调用。
综上,hert-gateway 接收的是 HTTP 请求,该请求的 handler 是解析参数发起了一个 RPC 请求到后端服务。 从整体代码结构上,整个项目如下
- 首先是
cmd
,它是所有的 RPC 服务的入口。多个应用也可以加不同的目录做区分,这里的代码很少,主要做应用启动、配置初始化、命令参数解析、依赖管理、底层框架强相关的初始化工作等等。 configs
该 demo 初始化环境时的一些配置,目前这里其实很简单,它创建了一个数据库,方便启动 demo 插入数据,直接去演示。hertz-gateway
,所有的网关逻辑放在了这里。idl
则是将所有的文件都放在此处。IDL 文件要强调的是,目前 Kitex 支持的泛化调用只支持 thrift 协议,之前也有小伙伴提出疑问能不能支持 gRPC,在我们社区内也接到了反馈,后续会进行考虑或者做相关规划。internal
是所有 RPC 微服务的业务逻辑。kitex_gen
是根据 IDL 生成的 Kitex 客户端代码。pkg
是一些公共代码。
hertz-gateway
我们具体看下 hertz-gateway
,基本上把所有的逻辑都在 biz
目录中。
biz
目录下再分如下几个目录:
errors
:规范错误处理。handler
:分发用户端请求,发起泛化调用到 RPC 服务的关键逻辑。middleware
:这里演示了签名的 middleware。router
由我们 hertz 框架自动生成的目录。types
存放自定义的数据,如 HTTP 响应体。main.go
程序入口。
整洁架构
在看 RPC 服务之前。我们有必要简单介绍下整洁架构
这里我们着重强调下整洁架构的核心的理念:图中红圈是依赖的抽象接口,圈里包括 usercase 和 Entities,这是整个业务的核心。 具体来说的三个要点:
- 独立于框架:系统的架构不应该与具体框架强绑定。
- 可被测试:单测不能依赖外部存储或者环境,单测必须能在本地,只依赖自己的代码来快速运行。
- 独立于数据库:不要写出强依赖你的存储组件的系统,底层的存储可能随着业务增长而不再使用。
另外, 还有一句时刻应该记住的话:“依赖抽象接口,不应该依赖具体实现“。这也是为什么依赖要显示地去注入,而不应该在你的业务逻辑中突然 New 出一个对象的原因。
Payment Server
目录如下:
makefile
放置了快速运行服务和开发时的常用命令。entity
即业务实体。usecase
核心的业务代码都会在这里去做,其依赖的抽象接口会放在这里定义。infrastructure
,包括 ORM 和仓储层的具体实现。
四、工程实践
在介绍了整个架构设计、分包、工程理念之后,来看具体的实践步骤。
- 第一步,抽象业务和定义 IDL。
- 第二步,创建一个微服务模块(Payment Server)
- 第三步,微服务工程目录。
- 第四步,网关和开放能力。
- 第五步,规范和细节调整。
抽象业务
刚刚也有提到,目前泛化调用只支持 thrift
协议。这里的 payment.thrift
以其中的一个接口为简单的演示:Unifypay 统一支付的接口,定义了它的入参和响应体。
其中我们看到 api.post
/ api.param
这些都是泛化调用时需要解析用到的注解,在官方文档也有更多详细的说明。
实现微服务
usecase
刚刚定义 IDL,现在去完善业务逻辑。比如在这里的场景,我们依赖抽象接口,插入订单记录,可以如下去做。
/internal/payment/usecase/service.go
interface
/internal/payment/usecase/interface.go
entity
下图是我们定义的业务实体,这也是我们的核心代码之一。
/internal/payment/entity/order.go
infrastructure
我们用 ent 作为 ORM,一个插入数据的代码如下:
/internal/payment/infrastructure/repository/order_sql.go
相关表设计放在单独的 ent 目录下
/internal/payment/infrastructure/ent/schema/order.go
网关
解析 IDL 并生成泛化调用客户端
网关的核心逻辑之一是:在服务启动时,如何解析 IDL,怎么生成方法调用的客户端,怎么放在路由组里面。这一段代码体现的这些过程,我们简单地介绍一下。
/hertz-gateway/router.go
- 第 3 行,定义一个路由组,它使用了网关签名的 middleware。
- 第 4 行,指定要遍历的 IDL 目录。
- 第 9 行,指定 RPC 服务的注册中心。
- 第 14 行,开始遍历解析 IDL。
- 第 17 行,解析具体的IDL,生成一个协议内容提供者。
- 第 22 行,构造 HTTP 类型的泛化调用策略。
- 到 26 行, 生成泛化调用的客户端,后续我们便可以据此发起调用。
发起泛化调用
/hertz-gateway/biz/handler/gateway.go
这里是我们在接收到用户请求后,路由了 /gateway/:svc
的 handler,处理如下:
- 第 13 行,svc 参数告知我们要请求到的 RPC 服务名。
- 第 14 行,SvcMap 是我们在启动服务时构建好的服务与泛化调用的客户端的映射关系。
- 从 16 - 19 行,构建泛化调用的请求参数。
- 第 21 行,发起一个泛化调用并拿到响应接口。
效果演示
- 详见回放视频(“01:04:15”处):meetings.feishu.cn/s/1iraydxgi…
- 也可参考 readme 步骤进行操作:github.com/cloudwego/b…
五、总结
关于项目
第一,该 demo 利用了 CloudWeGo 两个主要的项目 Kitex 和 Hertz。
第二,两个特性,泛化调用和整洁架构的核心理念。整洁架构简单易落地,对于大部分的业务抽象还是有指导意义的。
最后,演示理念,简洁和清晰。
关于工程化
CloudWeGo 最近新开源 cwgo ,是 Open Payment Platform 后续的一个优化点,结合更多生态内项目做演示更有意义。
cwgo 可以自定义工程化的模板,也欢迎更多人来贡献。工程模板或者工程化目录,可以用 DDD,或者用新的一些理念做划分,欢迎大家一起来探讨。
关于社区
最后分享一下我对社区的感受,我认为是自由、开放、彼此成就。
彼此成就,当你参与一个开源社区,让更多人用到、看到你贡献的代码和文档,不仅是帮助了别人,更是成就了自己,有一种自豪感和荣誉感。同时也帮助社区壮大和健康成长,这是双赢的效果。
很多人不知道怎么来贡献,其实贡献也包括很多种。文档,是很适合上手入门的,demo 也是其中一种,其他还包括单元测试和布道。很多人特别想去做特性和缺陷的开发,可以通过前几步,熟悉社区运作、开源协作方式,特别是通过这些路径熟悉现有项目,都是可以循序渐进而来的。
在社区有一句话:做开源,把时间拉长,三年五年投入一件事。积沙成塔,积水成渊。
参考
最后是以上内容的重要参考
- 泛化调用 www.cloudwego.io/zh/docs/kit…
- Proposal:工程化模板或标准化的讨论 github.com/cloudwego/k…
- Clean Architecture – The Dependency Rule codecoach.co.nz/clean-archi…
项目地址
-
GitHub:github.com/cloudwego
-
【CSG 第四期】CloudWeGo 业务实践案例解读开始啦