Go进阶训练营 – 微服务概览与治理二:微服务设计

API Gateway

B站进行了 SOA 服务化的架构演进,按照垂直功能进行了拆分,对外暴露了一批微服务,但是因为缺乏统一的出口面临了不少困难:

1.0

image-20220502191549104

  • 客户端到微服务直接通信,强耦合。
    • 如果涉及移动端,同时存在多版本,api兼容困难,重构困难,用户可能还在使用一年前版本的移动端。
  • 需要多次请求,客户端聚合数据,工作量巨大,延迟高。
    • 聚合数据应该在服务端做
      1. 服务端对自己提供的数据最了解,减少和客户端的沟通成本。
      2. 多次请求,网络开销大
    • 客户端使用的API,不应面向资源,应面向用户场景。服务端内部API应该面向资源,降低耦合性,提升可复用性
  • 协议不利于统一,各个部门间有差异,需要客户端来兼容。
    • 客户端不好控制,无法强制升级
    • 客户端发版难,不像服务端,不停机更新都可以
    • 前轻后重,移动端应该尽量轻量,因为发版受限,用户更新不可控,服务端可控性相对较高
  • 面向“端”的API适配,耦合到了内部服务。
    • 移动端适配难,不同移动端需要的字段不同,接口需要考虑兼容性
    • 多终端兼容逻辑复杂,每个服务都需要处理。
  • 统一逻辑无法收敛,比如安全认证、限流

2.0

image-20220502191615290

我们新增了一个 app-interface 用于统一的协议出口,在服务内进行大量的 dataset join,按照业务场景来设计粗粒度的 API,给后续服务的演进带来的很多优势:

  • 轻量交互:协议精简、聚合。
  • 差异服务:数据裁剪以及聚合、针对终端定制化API
  • 动态升级:原有系统兼容升级,更新服务而非协议。
  • 沟通效率提升:协作模式演进为移动业务+网关小组。

BFF(background for frontend) 可以认为是一种适配服务,将后端的微服务进行适配(主要包括聚合裁剪和格式适配等逻辑),向无线端设备暴露友好和统一的 API,方便无线设备接入访问后端服务。

  • 单体场景层
  • 感觉这里的网关层和场景层合二为一了

在计算机科学领域里的任何问题,都可以通过引入一个中间层来解决,不行就再加一层

...except for the problem of too many layers of indirection

存在的问题

最致命的一个问题是整个 app-interface 属于single point of failure(单点故障),严重代码缺陷或者流量洪峰可能引发集群宕机。

  • 单个模块也会导致后续业务集成复杂度高,根据康威法则,单块的无线BFF和多团队之间就出现不匹配问题,团队之间沟通协调成本高,交付效率低下。

    本来是场景层的多个场景,这里只有一个,所以这个BFF需要掌握所有业务,和组织机构不匹配。

3.0

image-20220502192108190

场景层拆分为多个场景

存在的问题

很多跨横切面逻辑,比如安全认证,日志监控,限流熔断等。随着时间的推移,代码变得越来越复杂,技术债越堆越多。

BFF包含了业务,每个服务都得加上上诉功能,所以臃肿

4.0

image-20220504175550325

抽取网关层

跨横切面(Cross-Cutting Concerns)的功能,需要协调更新框架升级发版(路由、认证、限流、安全),因此全部上沉,引入了 API Gateway,把业务集成度高的 BFF 层和通用功能服务层 API Gateway 进行了分层处理。

在新的架构中,网关承担了重要的角色,它是解耦拆分和后续升级迁移的利器。在网关的配合下,单块BFF 实现了解耦拆分,各业务线团队可以独立开发和交付各自的微服务,研发效率大大提升。另外,把跨横切面逻辑从BFF 剥离到网关上去以后,BFF的开发人员可以更加专注业务逻辑交付,实现了架构上的关注分离(Separation of Concerns)。

我们业务流量实际为:

移动端 -> API Gateway -> BFF -> Mircoservice,在FE Web业务中,BFF可以是nodejs 来做服务端渲染(SSRServer-Side Rendering),注意这里忽略了上游的 CDN4/7层负载均衡(ELB)

4层负载均衡

  • 传输层做负载均衡,客服端请求到负载均衡器,负载均衡器通过负载均衡算法匹配到服务端的地址,然后客户端与服务端直接连接,负载均衡器的作用相当于路由。

7层负载均衡

  • 应用层做负载均衡,客服端请求到负载均衡器,负载均衡器通过请求内容匹配到服务端,这里读取了请求内容,所以客户端已经和负载均衡器3次握手连接了。然后负载均衡器再去连接服务端,又是一次连接。负载均衡器的作用相当于代理服务器。
  • 功能更强大
  • 复杂意味着性能差
  • 安全性高,可防御DDos攻击

微服务拆分

微服务架构时遇到的第一个问题就是如何划分服务的边界。在实际项目中通常会采用两种不同的方式划分服务边界,即通过业务职能(Business Capability)或是 DDD 的限界上下文(Bounded Context)。

Business Capability

由公司内部不同部门提供的职能。例如客户服务部门提供客户服务的职能,财务部门提供财务相关的职能。

Bounded Context

限界上下文是DDD中用来划分不同业务边界的元素,这里业务边界的含义是“解决不同业务问题”的问题域和对应的解决方案域,为了解决某种类型的业务问题,贴近领域知识,也就是业务。

这本质上也促进了组织结构的演进:Service per team

如何拆分微服务,先按照业务领域,再考虑公司组织机构,避免出现一个服务多个部门负责。

刚开始的拆分可能会出现不合理的情况,和代码一样,都是不断重构,不断优化,才能做的更好。

CQRS

定义

Command and Query Responsibility Segregation。将应用程序分为两部分:命令端和查询端。命令端处理程序创建,更新和删除请求,并在数据更改时发出事件。查询端通过针对一个或多个物化视图执行查询来处理查询,这些物化视图通过订阅数据更改时发出的事件流而保持最新。

示例

image-20220530111707545

  • 在稿件服务演进过程中,我们发现围绕着创作稿件、审核稿件、最终发布稿件有大量的逻辑揉在一块,其中稿件本身的状态也有非常多种,但是最终前台用户只关注稿件能否查看,我们依赖稿件数据库 binlog 以及订阅binlog 的中间件 canal,将我们的稿件结果发布到消息队列 kafka 中,最终消费数据独立组建一个稿件查阅结果数据库,并对外提供一个独立查询服务,来拆分复杂架构和业务。

  • 我们架构也从 Polling publisher -> Transaction log tailing 进行了演进(Push vs Pull )。

  • 服务根据读写进行拆分。需要结合实际场景:1、对某个服务读操作和写操作的请求都有很多,整体服务庞大。2、写操作暴露出来比较危险,只想给部分服务调用,达到权限控制的目的。这时可以进行服务拆分,提供读操作的服务 watch 提供写操作的服务,也就是读写分离。

  • 为啥不直接数据库主从,因为读取服务并不是需要原数据库里的所有字段,只需要部分信息。所以同步逻辑需要自己实现,不能直接全部同步。

  • 读写分离的升级版。

微服务安全

image-20220530113439948

外网的请求

  1. 在 API Gateway 进行统一的认证拦截。

  2. 使用 JWT 方式通过 RPC 元数据传递的方式带到 BFF 层,BFF 校验 Token 完整性后把身份信息注入到应用的 Context 中。

    类似http请求里的header

  3. BFF 到其他下层的微服务,在 RPC Request 中带入用户身份信息(UserID)请求服务。

服务内部请求

一般要区分身份认证和授权。服务可以通过证书区分调用服务是哪个服务,可通过配置中心进行权限控制。

分为以下级别:

  • Full Trust
  • Half Trust
  • Zero Trust
    • 例如请求加密
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇