单体到微服务
为什么要转向微服务架构(收益)
- 系统扩展性不足(例如,因为DB连接问题导致无法持续扩容)
- 迭代效率低。如果是预估到业务在飞速增长,那就别犹豫,一定要提前考虑微服务的拆分。
- 系统编译、部署成本高
- 如果在设计架构的时候,发现需要很多异构的技术栈,那也要考虑下微服务。
转向微服务架构需要克服什么困难(成本)
- 技术基础设施要求比较高(如果公司技术基础设施非常完备,对应的业务起初就设计的非常复杂,那么也别犹豫,起手就上微服务。)
- 工程拆分挑战比较大,实现时容易为了拆分而拆分。
拆分原则
从单体到微服务,也许可以有一个过度:工程的拆分
微服务拆分的大原则
- 单一服务内的高内聚、低耦合,单一职责
- 拆分粒度:先粗拆,后细拆(伴随业务复杂度的变多,或者对业务的理解程度加深)
- 拆分过程中,要避免影响业务的日常功能迭代—按照依赖顺序,挨个拆分
- 确保服务接口定义有可扩展性
- 避免环形依赖问题(通过分层,例如业务层、数据访问层)
业务优先,组织结构,质量维度
SRP Single Responsibilty 单一指责原则
CCP Common Closure Principle 共同闭包原则,包中包含的所有类应该是同类的变化的一个集合,也就是说,如果对包作出修改,需要调整的类应该都在这个包之内。
你的团队成员结构是什么样的,你的架构就会长成啥样。
团队按照业务边界来拆分;确保团队不要太大
因为微服务就是为了减少研发成本,而包括沟通成本。
服务拆分带来的问题
- 接口调用耗时增加
- 如何知道调用哪个服务?服务注册中心
- 服务治理体系:熔断、限流、降级、超时控制等
- 问题排查困难(分布式链路追踪)
微服务组件
API网关
入口网关:隔离客户端与微服务,协议转换、安全策略、认证、限流、熔断等
出口网关:调用外部API,统一认证,授权、授权、访问控制等
性能:IO多路复用、异步非阻塞、线程池
设计要点:性能、扩展性(责任链模式)
隔离性:针对接口对线程池进行分类
服务注册与发现
zookeeper, consul, euraker
负载均衡
RoundRobin, Hash, Weight
熔断、限流
配置实时下发
客户端
配置项的读取-变更推送如何实现:
- 轮询 + 摘要 (简单,实时性略差)
- 长轮询 + 版本号(复杂,实时性好一些)
client的高性能实现逻辑:
- DoubleBufferedData, 数据分前台和后台
- 读拿到自己所在线程的thread-local读写锁,执行查询逻辑后释放锁。
- 同时只有一个写:修改后台数据,切换前后台,挨个获得所有thread-local锁并立刻释放,结束后再改一遍新后台(老前台)。
一个小原则:配置系统的旁路化,不要因为配置系统挂了,你的程序启动不了;
可以做两层缓存:内存缓存、文件保存
服务端
不同的配置类型:节点类型 》机房类型 》 全局配置
配置项的存储-配置系统的高可用
核心指标在于可用性!!!5个9?
对接调用链追踪 (Jager)
trace_id + span_id来标识链路的调用关系。
对trace_id采样,而不要随机采样。
span 基本工作单元,一次链路调用(可以是RPC,DB等没有特定的限制)创建一个span,通过一个64位ID标识它,uuid较为方便,span中还有其他的数据,例如描述信息,时间戳,key-value对的(Annotation)tag信息,parent_id(基于parent_span_id来维护树形结构)等,其中parent-id可以表示span调用链路来源。
trace_id 类似于 树结构的Span集合,表示一次完整的跟踪,从请求到服务器开始,服务器返回response结束,跟踪每次rpc调用的耗时,存在唯一标识trace_id。比如:你运行的分布式大数据存储一次Trace就由你的一次请求组成。
Annotation 注解,用来记录请求特定事件相关信息(例如时间),一个span中会有多个annotation注解描述。通常包含四个注解信息:
监控系统
方案:普罗米修斯、Graphna
APM:端到端的监控体系
如何防止消息被篡改:对消息体 + 消息头 进行加密,生成一个签名
如何对数据进行加密:
使用非对称加密的公钥对 “对称加密的私钥-OriginPrivate“ 进行加密,得到SecretPrivate
然后服务端利用非对称加密的私钥,对 SecretPrivate进行解密,得到OriginPrivate
然后再使用OriginPrivate对加密之后的消息体(SecretContext)进行解密,得到Contenxt
监控哪些东西:网络卡顿率、做某件事情的失败率等
考虑暂存 + retry来应对网络状况不佳的情况
自动化全链路压测系统
压测的原则-尽量模拟真实情况;压测的注意点:
(1)使用线上数据与线上数据
(2)使用线上流量(流量拷贝)
(3)流量应该从尽量靠近用户的CDN发起
如何搭建:
(1)流量的隔离(区分压测流量与正式流量)
(2)风险控制(尽量避免压测对正常用户的影响)
压测数据的产生:
拷贝真实流量(可以从访问日志、可以抓取某个端口的数据等)
打上压测标签
放在合适的机房(尽量接近用户)
数据隔离:
针对读请求,针对某些不能压测(例如推荐、数据分析等)的组件进行mock
对于写请求,把流量产生的数据写入影子库(数据库-拷贝一份库表和数据;缓存-加压测前缀;ES-多搞一份索引)
压力测试的实施
持续放大,做好系统过载的识别(例如超时率、resp time等)