微服务
分布式与微服务的异同:
- 分布式一般以机器为单位,不同机器上部署不同的模块;微服务一般以进程为单位,每个进程是一个服务
- 服务的粒度比模块粒度小
- 微服务属于分布式,只不过是粒度更小
微服务简介
围绕业务功能构建的,通过业务拆分实现服务组件化,服务关注单一业务,不同服务可组合来实现更复杂的业务,更灵活;服务间采用轻量级的通信机制
优点
- 每个模块的代码量相对较小,易于维护
- 每个模块具有单一职责,逻辑清晰
- 独立进程,隔离部署 方便容器化
- 去中心化服务治理
- 数据去中心化:每个服务独享自身的数据存储设施(缓存、数据库),有利于服务的独立性
- 治理去中心化:去全局热点
- 技术去中心化:每个服务可使用自己的技术(语言、中间件等)
ps:热点问题:“热"即访问频率高,“点"即一个地方,热点就是访问频率高的一个地方
缺点
- 基础设施的建设复杂
- 分布式一致性问题
基础设施自动化
微服务的系统复杂度较高,需要一套自动的测试和部署工具
- CI/CD:gitlab + gitlab hooks + k8s
- Testing:测试环境、单元测试、API自动化测试
- 在线运行时:k8s,及一系列的Prometheus、ELK、control panel
可用性 & 兼容性
记住design for failure思想,所有的代码或设计都可能失败
- 隔离
- 超时控制
- 负载保护
- 限流
- 降级
- 重试
- 负载均衡
尽量采用粗粒度的进程间通信,比如rpc请求时,不要多次请求一条数据,而是一次请求一批数据(减轻网络压力)
接口兼容性:发送时保守(最小化传输必要信息),接收时开放(最大限度容忍冗余数据)
微服务设计
API Gateway网关

以上图片是第一版,直接将业务垂直拆分,每个服务提供多个面向资源的RESTful API
缺点:前后端强耦合,前端一个业务要关联多个后端API;前端需要多次请求(因需要多个API)
重点:将面向资源的API转换为面向业务的API(底层API的组合),不要让前端来进行API组装,要前轻后重

以上图片是第二版,加入了基础的网关,后端和网关对接,网关统一业务接口
优点:轻量交互、差异服务
缺点:单点故障
这种架构称为BFF(backend for frontend):后端组装数据,进行数据编排,为前端提供服务

以上图片是第三版,网关拆分,不同组件针对不同的业务
缺点:对跨横切面的功能来说变得复杂,如路由、安全、限流、日志,若每个组件都实现一遍则会很复杂

以上图片是第四版,加入了API Gateway,用来处理跨横切面的功能
整个架构可分为以下几层:
- 负载均衡
- API GateWay:实现跨横切面的功能
- BFF:数据组装、编排,实现面向业务的API
- 原子服务:提供面向资源的API
微服务如何划分
- 按业务来组织服务:即拆分后的服务所提供的能力要与业务功能对应。如订单服务和推荐服务,但订单服务和数据服务则不是一种好的拆分
- DDD(Domain-Driven Design 领域驱动设计)的有界上下文
- CQRS:命令查询分离,类似读写分离
整个架构分成无状态和有状态两个部分,业务逻辑的部分作为无状态的部分,很容易的横向扩展,在用户分发的时候,可以很容易分发到新的进程进行处理;状态保存在后端有状态的中间件中,如缓存、数据库、对象存储、大数据平台、消息队列等,这些中间件设计之初,就考虑了扩容时状态的迁移、复制、同步等机制,不用业务层关心。
微服务的无状态化是为了将应用服务更好的扩容,和原子性操作无关,要看服务是否要保留之前的交互数据,如果完全不需要保存,或者统一放到了分布式的数据库中,就是无状态的。
微服务安全
外网:用户鉴权
内网:服务之间不能乱调,要进行权限认证,要知道调用我的是什么
gRPC & 服务发现
grpc优势:
- 支持多语言
- 基于http2,二进制、支持双向流、单TCP的多路复用、服务端推送等特性
- 使用protocol buffer定义服务
- 使用protocol buffer序列化,比单纯的json效率高
- grpc有标准的健康检测协议
与http的不同: grpc是关注于服务调用的,而http是关注于传输的;另外grpc是强约束的,消息和服务都是用pb定义好的,而http的参数则比较随意;另外对http的连接可能还要手动维护连接池等,但grpc都自己做好了
grpc 客户端流式,服务端流式,双向流式,在应用中的具体实例:
- 客户端流式:请求里 req 包比较大,但 response 包比较小
- 服务端流式:请求里 req 包比较小,但 response 包比较大,可以用于定时任务拉取分页信息,缓存中拉取全量数据等。包太大一次返回对服务压力很大,所以在不要求对时间情况下,可以通过流式返回
- 双向流式:比如 im 通信,watch 事件
健康检测HealthCheck

健康检测是指检测服务是否正常运行,k8s可以检测,grpc也可以检测,相同之处为两者都可以判别服务是否正常,不同之处在于探测时所经过的网络链路不同,k8s是注册中心到服务,grpc是服务调用者到服务,若服务到服务调用者之间的链路故障,但k8s探测后为正常,那么此时服务调用就会超时,但grpc检测是经过此条链路的,若故障则会报告服务不正常,会将此服务节点下线或故障转移,而不会导致服务超时
健康检测还可用于滚动更新:平滑下线与平滑发布
滚动更新:下线旧版本,上线新版本,所以需要平滑下线与上线,一下是结合healthcheck的平滑发布与下线
平滑下线步骤:
- 在管理平台(如k8s)上点击下线按钮,会发送SIG_TERM信号给应用,应用在收到退出信号后会先向服务发现中心注销服务,随后服务发现中心便会通知各个consumer此节点不再有效,consumer将关于此节点的链接关闭,当provider中没有连接时则进行下线退出。但通知是需要时间的,所以provider也可以让healthcheck接口置为失败状态,若consumer发现接口不通则也进行相应的下线处理。但若有一些bad case,如consumer一直保持着与provider之间的连接,则此时管理平台(如k8s)会有一个下线超时(10-60s),若超时则强制退出
平滑发布步骤:
- 一个服务(provider)上线以后,什么时间向服务发现中心注册这个服务信息呢?做法是provider有一个healthcheck的接口,外部的工具(如k8s)会来调用这个接口,如果这个接口通了,就会向服务发现中心进行注册,这种服务注册方式也成为外挂注册
kill pid 默认发送SIG_TERM信号,kill -9 pid 发送SIG_KILL信号,TERM信号会被应用catch,应用可以选择退出或不退出或在退出前进行一定的处理,而KILL信号应用却catch不住,所以相当于强杀
服务发现
客户端发现:

客户端查服务的注册表,然后将这些服务通过负载均衡来直接连接
直连较快,但客户端各个语言都要实现一遍服务发现
服务端发现:

客户端只需连接到负载均衡器即可,服务发现和负载均衡均由负载均衡器(服务端)来处理

服务端发现有个缺点就是比较复杂
service mesh
服务网格是一个基础设施层,用于处理服务间通信。云原生应用有着复杂的服务拓扑,服务网格保证请求在这些拓扑中可靠地穿梭。在实际应用当中,服务网格通常是由一系列轻量级的网络代理组成的,它们与应用程序部署在一起,但对应用程序透明。
简言之:将负载均衡和服务发现等功能与应用服务部署在一起,即在一个pod中
优点:
- 屏蔽分布式系统通信的复杂性(负载均衡、服务发现、认证授权、监控追踪、流量控制等等),服务只用关注业务逻辑;
- 真正的语言无关,服务可以用任何语言编写,只需和Service Mesh通信即可;
- 对应用透明,Service Mesh组件可以单独升级;
缺点:
- Service Mesh组件以代理模式计算并转发请求,一定程度上会降低通信系统性能,并增加系统资源开销;
- Service Mesh组件接管了网络流量,因此服务的整体稳定性依赖于Service Mesh,同时额外引入的大量Service Mesh服务实例的运维和管理也是一个挑战;
多租户
多租户如测试用户和正常用户,B`是待测试的功能节点,可通过header标记进行路由,测试用户路由到B`,正常用户路由到B
也就是给流量打上不同的标签,如果某些服务有新功能需要测试就拿到此标签的流量进行处理
本质就是路由


其他
提升架构性能

提高稳定性方式

核心技术

有状态的服务调度:
- 对于应用层上的分布式事务一致性,只有两阶段提交这样的方式。
- 而底层存储可以解决这个问题的方式是通过一些像 Paxos、Raft 或是 NWR 这样的算法和模型来解决。


