微服务设计思路
2021-10-11 16:25:48 1 举报
AI智能生成
微服务设计思路
作者其他创作
大纲/内容
跨语言平台,组织内外皆可
使用场景
使用了HTTP通信协议,相比TPC来说,性能较差
缺点
RESTful API
服务提供者定义接口,并实现接口
服务提供者启动,加载server.xml暴露接口
服务消费者启动,加载client.xml引入调用的接口
原理
多在私有RPC协议使用,一般用作组织内部,性能优越
代码入侵性强,配置繁琐
不支持跨语言平台
XML配置
接口描述语言 Interface Description Language
跨语言平台的服务之间调用,组织内外皆可
文件描述太繁琐
修改或删除PB字段不能向前兼容
Thrift协议 (Facebook开源)
gRPC协议 (Google开源)
代表
IDL文件
发布和引用服务
RPC Server 提供服务,在启动时,根据服务发布文件 server.xml 中的配置的信息,向 Registry 注册自身服务,并向 Registry 定期发送心跳汇报存活状态。
RPC Client 调用服务,在启动时,根据服务引用文件 client.xml 中配置的信息,向 Registry 订阅服务,把 Registry 返回的服务节点列表缓存在本地内存中,并与 RPC Sever 建立连接。
当 RPC Server 节点发生变更时,Registry 会同步变更,RPC Client 感知后会刷新本地内存中缓存的服务节点列表。
RPC Client 从本地缓存的服务节点列表中,基于负载均衡算法选择一台 RPC Sever 发起调用。
注册中心原理
服务注册接口
服务注销接口
心跳汇报接口
服务订阅接口
服务变更查询接口
服务信息查询接口
服务信息修改接口
注册中心API
每个 Server 在内存中存储了一份数据,Client 的读请求可以请求任意一个 Server。
ZooKeeper 启动时,将从实例中选举一个 leader(Paxos 协议)。
Leader 负责处理数据更新等操作(ZAB 协议)。
一个更新操作成功,当且仅当大多数 Server 在内存中成功修改 。
工作原理
以Zookeeper为例
为了实现异地多活,有的注册中心还采用多 IDC 部署
高可用性-集群部署
高一致性-分布式一致性协议
集群部署
每个目录在 ZooKeeper 中叫作 znode,并且其有一个唯一的路径标识。
znode 可以包含数据和子 znode。
znode 中的数据可以有多个版本,比如某一个 znode 下存有多个数据版本,那么查询这个路径下的数据需带上版本信息。
目录存储
基于客户端和服务端的长连接和会话超时控制机制实现
在 ZooKeeper 中,客户端和服务端建立连接后,会话也随之建立,并生成一个全局唯一的 Session ID。服务端和客户端维持的是一个长连接,在 SESSION_TIMEOUT 周期内,服务端会检测与客户端的链路是否正常,具体方式是通过客户端定时向服务端发送心跳消息(ping 消息),服务器重置下次 SESSION_TIMEOUT 时间。如果超过 SESSION_TIMEOUT 后服务端都没有收到客户端的心跳消息,则服务端认为这个 Session 就已经结束了,ZooKeeper 就会认为这个服务节点已经不可用,将会从注册中心中删除其信息。
服务健康状态检测
Watcher 机制
服务消费者在调用 ZooKeeper 的 getData 方法订阅服务时,还可以通过监听器 Watcher 的 process 方法获取服务的变更,然后调用 getData 方法来获取变更后的数据,刷新本地缓存的服务节点信息。
服务状态变更通知
在实际应用中,注册中心可以提供一个白名单机制,只有添加到注册中心白名单内的 RPC Server,才能够调用注册中心的注册接口,这样的话可以避免测试环境中的节点意外跑到线上环境中去。
白名单机制
注册和发现
原理:三次握手 四次挥手
HTTP
ServerSocket 通过调用 bind() 函数绑定某个具体端口,然后调用 listen() 函数实时监控网络状态,等待客户端的连接请求。
服务器监听
ClientSocket 调用 connect() 函数向 ServerSocket 绑定的地址和端口发起连接请求。
客户端请求
当 ServerSocket 监听到或者接收到 ClientSocket 的连接请求时,调用 accept() 函数响应 ClientSocket 的请求,同客户端建立连接。
服务端连接确认
当 ClientSocket 和 ServerSocket 建立连接后,ClientSocket 调用 send() 函数,ServerSocket 调用 receive() 函数,ServerSocket 处理完请求后,调用 send() 函数,ClientSocket 调用 receive() 函数,就可以得到得到返回结果。
数据传输
客户端需要定时地发送心跳检测消息(一般是通过 ping 请求)给服务端,如果服务端连续 n 次心跳检测或者超过规定的时间都没有回复消息,则认为此时链路已经失效,这个时候客户端就需要重新与服务端建立连接。
链路存活检测
通常有多种情况会导致连接断开,比如客户端主动关闭、服务端宕机或者网络故障等。这个时候客户端就需要与服务端重新建立连接,但一般不能立刻完成重连,而是要等待固定的间隔后再发起重连,避免服务端的连接回收不及时,而客户端瞬间重连的请求太多而把服务端的连接数占满。
断连重试
针对网络不可靠性的处理手段
Socket
建立网络连接
客户端每发一次请求,服务端就生成一个线程去处理。当客户端同时发起的请求很多时,服务端需要创建很多的线程去处理每一个请求,如果达到了系统最大的线程数瓶颈,新来的请求就没法处理了。
BIO
客户端每发一次请求,服务端并不是每次都创建一个新线程来处理,而是通过 I/O 多路复用技术进行处理。就是把多个 I/O 的阻塞复用到同一个 select 的阻塞上,从而使系统在单线程的情况下可以同时处理多个客户端请求。这种方式的优势是开销小,不用为每个请求创建一个线程,可以节省系统开销。
NIO
客户端只需要发起一个 I/O 操作然后立即返回,等 I/O 操作真正完成以后,客户端会得到 I/O 操作完成的通知,此时客户端只需要对数据进行处理就好了,不需要进行实际的 I/O 读写操作,因为真正的 I/O 读取或者写入操作已经由内核完成了。这种方式的优势是客户端无需等待,不存在阻塞等待问题。
AIO
使用开源方案 Netty
处理请求
Dubbo
数据传输协议
文本类 XML/JSON
二进制类 PB/Thrift
Hession
JAVA
序列化和反序列化
RPC远程服务调用
用户端监控
接口监控
Redis等
资源监控
CPU 利用率、内存使用量、I/O 读写量、网卡带宽等
基础监控
监控对象
QPS
实时请求量
PV (Page View)
统计请求量
请求量
把响应时间划分为多个区间,比如 0~10ms、10ms~50ms、50ms~100ms、100ms~500ms、500ms 以上这五个区间。
正常情况下,这个区间内的请求数应该接近于 0
在出现问题时,这个区间内的请求数会大幅增加,可能平均耗时并不能反映出这一变化
比如 P99 = 500ms,意思是 99% 的请求响应时间在 500ms 以内
P90 | P95 | P99 |P999
重点关注500ms以上
响应时间
比如对于接口的错误率一般用接口返回错误码为 503 的比率来表示。
错误率的监控通常用一段时间内调用失败的次数占调用总次数的比率来衡量
错误率
监控指标
全局维度
分机房维度
单机维度
时间维度
核心业务和非核心业务在部署上必须隔离,分开监控,这样才能对核心业务做重点保障。
核心维度
监控维度
服务主动上报
代理收集
一般来说,采样率越高,监控的实时性就越高,精确度也越高。但采样对系统本身的性能也会有一定的影响,尤其是采集后的数据需要写到本地磁盘的时候,过高的采样率会导致系统写入磁盘的 I/O 过高,进而会影响到正常的服务调用。所以设置合理的采用率是数据采集的关键,最好是可以动态控制采样率,在系统比较空闲的时候加大采样率,追求监控的实时性与精确度;在系统负载比较高的时候减小采样率,追求监控的可用性与系统的稳定性。
指标:采样率
数据采集
数据处理单元提供服务器的请求地址,数据采集后通过 UDP 协议与服务器建立连接,然后把数据发送过去。
UDP
数据采集后发送到指定的 Topic,然后数据处理单元再订阅对应的 Topic,就可以从 Kafka 消息队列中读取到对应的数据。
Kafka
最常用的就是 PB 对象,它的优点是高压缩比和高性能,可以减少传输带宽并且序列化和反序列化效率特别高。
二进制协议 PB
最常用的就是 JSON 字符串,它的优点是可读性好,但相比于 PB 对象,传输占用带宽高,并且解析性能也要差一些。
文本协议 JSON
数据传输格式
接口维度聚合
机器维度聚合
数据聚合
索引数据库 ES
时序数据库 OpenTSDB
持久化
数据处理
曲线图
饼状图
格子图
数据展示
监控系统原理
监控服务调用
优化系统瓶颈
如找出跨数据中心的调用
优化链路调用
生成网络拓扑图
如A/B测试
透明传输数据
作用
请求的全局唯一ID
traceId
定位请求所处的位置
spanId
如用户名
用户自定义埋点数据
annotation
客户端发起请求,并生成调用的上下文。
CS(Client Send)阶段
服务端接收请求,并生成上下文。
SR(Server Recieve)阶段
服务端返回请求,这个阶段会将服务端上下文数据上报
traceId=123456,spanId=0.1,appKey=B,method=B.method,start=103,duration=38。
SS(Server Send)阶段
客户端接收返回结果,这个阶段会将客户端上下文数据上报,
traceid=123456,spanId=0.1,appKey=A,method=B.method,start=103,duration=38。
CR(Client Recieve)阶段
A→B分为4个阶段
数据埋点上报
数据采集层
聚合加工 Storm/Spark Streaming
Hbase
存储 OLTP
实时数据处理
MapReduce
离线计算
存储 Hive
离线数据处理
数据处理层
服务整体情况
每一层的情况
调用链路图
服务关系
服务依赖调用的QPS、平均耗时等
调用拓扑图
数据展示层
实现
服务调用追踪
这种机制要求服务提供者定时的主动向注册中心汇报心跳,注册中心根据服务提供者节点最近一次汇报心跳的时间与上一次汇报心跳时间做比较,如果超出一定时间,就认为服务提供者出现问题,继而把节点从服务列表中摘除,并把最近的可用服务节点列表推送给服务消费者
注册中心主动摘除机制
虽然注册中心主动摘除机制可以解决服务提供者节点异常的问题,但如果是因为注册中心与服务提供者之间的网络出现异常,最坏的情况是注册中心会把服务节点全部摘除,导致服务消费者没有可用的服务节点调用,但其实这时候服务提供者本身是正常的。所以,将存活探测机制用在服务消费者这一端更合理,如果服务消费者调用服务提供者节点失败,就将这个节点从内存中保存的可用服务提供者节点列表中移除。
服务消费者摘除机制
节点管理
推荐后端服务节点的配置没有差异,同等调用量下性能也没有差异的情况下使用
随机算法
就是按照固定的权重,对可用服务节点进行轮询。如果所有服务节点的权重都是相同的,则每个节点的调用量也是差不多的。但可以给某些硬件配置较好的节点的权重调大些,这样的话就会得到更大的调用量,从而充分发挥其性能优势,提高整体调用的平均性能。
轮询算法
这种算法是在服务消费者这一端的内存里动态维护着同每一个服务节点之间的连接数,当调用某个服务节点时,就给与这个服务节点之间的连接数加 1,调用返回后,就给连接数减 1。然后每次在选择服务节点时,根据内存里维护的连接数倒序排列,选择连接数最小的节点发起调用,也就是选择了调用量最小的服务节点,性能理论上也是最优的。
后端服务节点存在比较明显的配置和性能差异的情况下使用
最少活跃调用算法
指相同参数的请求总是发到同一服务节点。当某一个服务节点出现故障时,原本发往该节点的请求,基于虚拟节点机制,平摊到其他节点上,不会引起剧烈变动。
一致性Hash算法
算法
负载均衡
业务存在灰度发布的需求
多机房就近访问的需求
就是在服务消费者本地存放服务调用的路由规则,在服务调用期间,路由规则不会发生改变,要想改变就需要修改服务消费者本地配置,上线后才能生效。
静态配置
这种方式下,路由规则是存在注册中心的,服务消费者定期去请求注册中心来保持同步,要想改变服务消费者的路由配置,可以通过修改注册中心的配置,服务消费者在下一个同步周期之后,就会请求注册中心来更新配置,从而实现动态更新。
动态配置
配置方式
服务路由
就是服务消费者发现调用失败或者超时后,自动从可用的服务节点列表总选择下一个节点重新发起调用,也可以设置重试的次数。这种策略要求服务调用的操作必须是幂等的,也就是说无论调用多少次,只要是同一个调用,返回的结果都是相同的,一般适合服务调用是读请求的场景。
FailOver-失败自动切换
就是服务消费者调用失败或者超时后,不立即发起重试,而是隔一段时间后再次尝试发起调用。比如后端服务可能一段时间内都有问题,如果立即发起重试,可能会加剧问题,反而不利于后端服务的恢复。如果隔一段时间待后端节点恢复后,再次发起调用效果会更好。
FailCache-失败缓存
幂等
就是服务消费者调用失败或者超时后,不再重试,而是根据失败的详细信息,来决定后续的执行策略。比如对于非幂等的调用场景,如果调用失败后,不能简单地重试,而是应该查询服务端的状态,看调用到底是否实际生效,如果已经生效了就不能再重试了;如果没有生效可以再发起一次调用。
FailBack-失败通知
就是服务消费者调用一次失败后,不再重试。实际在业务执行时,一般非核心业务的调用,会采用快速失败策略,调用失败后一般就记录下失败日志就返回了。
FailFast-快速失败
非幂等
服务容错
服务治理手段
微服务
收藏
0 条评论
回复 删除
下一页