微服务架构原理 | 青训营笔记

72 阅读7分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 5 天

微服务架构介绍

系统架构演变历史

为什么系统架构需要演进?

  • 互联网爆炸性发展

    • 用户规模指数级增长
    • 需求复杂
  • 硬件设置的快速发展

  • 需要复杂性的多样化

  • 开发人员的急剧增加

  • 计算机理论及技术的发展

总的来说就是老的系统架构无法满足新的需求

演变历史

单体架构->垂直应用架构->分布式架构->SOA架构->微服务架构

单体架构

all in one process

把所有的逻辑放在一个项目里面

优势:

  1. 性能最高
  2. 冗余小

劣势:

  1. debug困难:当一个项目由上万人同时研发,有几十个上百个模块的时候,就已经无法再debug了
  2. 模块相互影响:假设广告管理模块有一个bug,可能就会导致整个项目崩溃
  3. 模块分工、开发流程:当该项目有几十人或上百人共同开发的时候,假设一个功能模块开发受阻,其他模块也会受到阻碍

垂直应用架构

思想:按照业务线垂直划分

如图的项目可以分为:电商业务线,后台业务线,广告业务线

每个业务线又回到了单体结构

优势:

  1. 业务独立开发维护

劣势:

  1. 不同业务存在冗余
  2. 每个业务还是单体

分布式架构

思想:抽出业务无关的公共模块

本项目抽出了与业务无关的服务层,达到逻辑服用的功能

优势:

  1. 业务无关的独立服务:公共服务的去冗余,实现复用

劣势

  1. 服务模块bug可导致全站瘫痪
  2. 调用关系复杂:如图可知,模块对公共服务的调用是N:N的,那么就导致了单个服务的bug导致整个项目瘫痪
  3. 不同服务冗余:服务本身仍存在冗余,无法去除

SOA架构(Service Oriented Architecture)

思想:面向服务

可以看到,相比分布式架构多了一个服务注册中心,通过服务注册中心解耦

优势

  1. 服务注册

劣势

  1. 整个系统设计是中心化的
  2. 需要从上至下设计
  3. 重构困难

微服务架构

思想:彻底的服务化

优势

  1. 开发效率
  2. 业务独立设计
  3. 自下而上
  4. 故障隔离

劣势

  1. 治理、运维难度
  2. 观测挑战
  3. 安全性
  4. 分布式系统

微服务架构概览

Actor:用户终端

网关:处理外界流量

链路追踪和监控:微服务架构本身是一个非常庞大的分布式系统,在此基础上进行问题定位,需要同一台的平台、能力来支持

微服务架构核心要素

  • 服务治理

    • 服务注册
    • 服务发现
    • 负载均衡
    • 扩缩容
    • 流量治理
    • 稳定性治理
    • ......
  • 可观测性

    • 日志采集
    • 日志分析
    • 监控打点
    • 监控大盘
    • 异常报警
    • 链路追踪:微服务是链式调用,跨主机的链路追踪提供问题排查
    • ......
  • 安全

    • 身份验证
    • 认证授权
    • 访问令牌
    • 审计
    • 传输加密
    • 黑产攻击
    • ......

微服务架构原理及特征

基本概念

在上图中,一段代码通过编译、部署,生成了一个服务,服务里有一个集群,集群中包含多个实例

  • 服务(service) :一组具有相同逻辑运行实体。(运行同一段代码的多个实例)

  • 实例(instance) :一个服务中,每个运行实体即为一个实例。

  • 实例与进程的关系:实例与进程之间没有必然对应关系,可以一个实例可以对应一个或多个进程(反之不常见)

  • 集群(cluster):通常指服务内部的逻辑划分,包含多个实例。

  • 常见的实例承载形式:进程、VM、k8s pod……

  • 有状态/无状态服务:服务的实例是否存储了可持久化的数据(例如磁盘文件)

如果把HDFS看作一组微服务,由于NameNode和DataNode运行不同的代码,因此被分为两个服务

NameNode是一个单实例服务,而DataNode服务具有多个实例

服务间通信

对于单体服务,不同模块通信只是简单的函数调用

对于微服务,服务间通信息意味着网络传输,包括HTTP,gRPC、Thrift等

在企业内部,HTTP和ROC协议的使用是比较广泛的

在上图中,一个服务包含若干个实例,服务与服务之间通过网络传输进行通信

服务注册与发现

在代码层面,如何制定调用一个目标服务的地址(ip:port)?

hardcode

hardcode(硬编码,将本应写在配置文件中的内容在程序中写死)

//Service A wants to call service B
client = grpc.NewClient("10.23.45.67:8080")

存在什么问题?

不太可能指定一个固定的IP地址,这种地址是动态变化的

而且一个服务包含多个实例,指定一个IP地址显然是不够的

如上图所示,服务B中的三个实例运行同一份代码

服务A通过IP地址显然只调用了服务B的一个实例,这样是不够的,这样其他两个实例就会很轻松了,不能让他们得逞

还能采用什么中间形式来表达服务?

DNS

如图所示,服务A通过域名调用服务B,服务B的三个IP地址通过NameService登记到域名当中去,这样当服务B的IP地址发生变化时,只需修改域名记录即可,不会出现只返回一个实例的问题

存在什么问题?

  • 本地DNS存在缓存,导致延时:本地DNS有缓存机制,当IP地址发生变化需要刷新
  • 负载均衡问题:DNS仍然可能只返回第一个IP,仍然存在过载问题
  • 不支持服务实例的探活检查
  • 域名无法配置端口:这就导致了实例的数量是有一定上限的

服务注册中心

解决思路:新增一个统一的服务注册中心,用于存储服务名到服务实例的映射(简单理解为哈希表或map)

在上图中服务A中的代码

//调用服务注册中心拿到服务B的地址
adds = svc_reg.find("service.b")
//调用一个负载均衡算法random随机调用其中一个实例
net.Dial(addsrs[random(n)])

服务的上线和下线

假设已经有了一个服务注册中心,两个服务,各有三个实例,3:3的网络通信

若管理员发现服务B的实例3有问题,需要下线,如何实现

若直接下线,则发过来的请求就会失败,造成线上产品故障

基于服务发现的技术会执行一下操作

首先管理员告诉注册中心,把实例3的记录删掉

服务A会不断刷新服务发现

过几分钟后,实例A就不会再去调用服务B的实例3

这个时候再去删除实例3就已经是安全的了

这个时候服务B的压力就会比较大,需要上线新的实例

先添加实例到服务B,执行健康检查(测试进程能否接受TCP建连请求)

检查OK之后,再去向注册中心添加实例记录

注册之后,服务A还是会不断地刷新服务发现,这时发现服务发现的第三个实例出现了,流量恢复

流量特征

前面基于组件的维度,我们了解了微服务的架构包含服务、实例、服务注册中心等部分

接下来从上图流量的维度观察微服务的结构

首先是终端用户,可能还涉及到CDN

流量进入右侧Internet企业内网环境之后,首先会经过负载均衡(Load Balance),API网关,最后在分配到各个服务上去

这里要注意区分流量是通过HTTP传输还是RPC传输

HTTP是一个文本协议,传输的是文本,性能不高,而RPC是通过二进制的形式传输,无论是信息传输还是编解码效率都是很高的,因此内部之间的通信采用RPC协议

Offline Training Job通常做推荐工作,在执行推荐系统、机器学习等训练模型训练的同时,需要调用一些其他的模块

  • 统一网关入口
  • 内网通信多数采用RPC
  • 网状调用链路