大家好
我们开始Go进阶训练营第一课
微服务治理与概览
那我们开始Go的课程
为什么要先讲
微服务和治理相关的一些东西呢
主要是我觉得从
整个后端的一些架构演进里面
因为我们最终Go要用到服务端的一个开发
和一个工作中把微服务和Go结合的话
是非常重要的一个点
所以我们其实是结合微服务
告诉大家有个完整的一个架构
有一个完整的服务端的研发
因为我们用Go主要是做业务
或者是后端微服务的一些开发工作
所以说这个其实是非常重要
就介绍一些我们在微服务的一些演进中
或者是微服务可用性设计等等这方面的一些知识
然后再过渡到我们Go在工程化
或者是我们用Go去实现一个微服务
实现一个后端的一个工程
可能需要做一些什么样的一些知识储备
所以这块是非常重要的
会分为四块来讲
我会整体介绍一下微服务的一些基本的概念知识
因为微服务内部还分了很多角色
比方说有网关
有BFF然后有一些内部的一些Micro Service
这些可能是我们需要去了解的
那么微服务设计的话
其实也涉及到各个组件之间如何去协作和协调
那么第三块的话我们会去讲一讲
微服务里面最重要的就是RPC框架
然后以及RPC最重要的一个组件就是服务发现
因为我们知道随着规模越来越大以后
业务的进程越来越多
肯定是需要一些地址的发现服务
那么最后一块其实就是微服务里面的
多集群的概念包括多租户
我知道大家可能在日常工作中里面
经常可能会遇到我的一个业务服务需要
多环境的一个部署测试
包括上线因为我们上线可能还需要
做一些预发灰度等等各种各样的验证
这些可能就是需要微服务的一个多租户的场景
这四块就是我们包含微服务的一些基础的知识
我们先看一下
通常的一个业务开发过程中是怎么演进的
举个例子我之前在B站工作
早期的时候规模比较小
那么当然是我们如何简单就如何来开发
那这种情况下
我们就会形成一个单体架构
我们看到右边这张图
一个传统的一个web application
一个web应用
其实就是像浏览器然后到Apache
然后Apache会到我们一个所谓的巨石的一个应用
就是一个war包
那里面包含什么全家桶全部在里面
然后可能简单地访问一下数据库
因为这种模型的话早期的话
它是相对来说比较容易测试开发部署扩展
相对来说是比较简单
因为你的业务本身体量是
不是特别复杂的
随着我们初具规模以后
它变成一个我们喜欢称它为全家桶
因为什么东西都揉在里面
然后会变得无比的复杂
也就是说经常我们可能遇到一种情况
你可能改明明只想改A功能
但是不知道为什么会影响到BCD其它的一些模块
然后它可能也会受到影响
所以说这些其实是非常痛苦的
包括再到后面的话
整个代码库代码体积非常庞大
导致我们整个IDE是非常卡顿的
它的整个调试部署其实变得越来越复杂
因为它整个项目的依赖是非常混乱的
尽管我们是在巨石架构里面
也是去有一个模块化的逻辑
但是最终的话
它是会被打包成一个整体的应用去部署的
那么它对于性能的一个扩展
因为我们整个单体里面可能只有一小部分
它存在一个业务的一些热点
那么你要去扩容整个单体架构整个单体应用的话
它其实带来的收益并不会那么高
而且说对我们单个开发者来说
你去整个交付和开发它其实也不太容易搞懂
这个就是我们实际都会遇到的一些问题
最终就变成应用无法扩展
可靠性非常低
最终影响我们的开发效率
包括部署的时候启动5分钟这种
相信大家都有这种感触
因为加载很多这种jar包war包
整个启动的时间是非常久的
我们通常在应对一个复杂的架构
我们都知道化繁为简
分而治之
把一个复杂的东西
把它变成一个一个模块化的东西
或者一小块一小块的东西
然后去完成一个这样的眼镜
我们再看到下面这张图
这个是我加入B站以后
我们主导的一个从单体架构到
我们叫分布式应用
或者叫微服务应用的一个演进吧
我们看到其实它已经拆成了
每一个框都是一个服务或者一个进程
那么我们其实把整个
复杂业务形态变成了一个一个的小的应用
应用与应用之间
因为每个人专注的是一个单一的一个功能
或者是服务
然后他们之前进行一些业务的一些组装或者编排
最终形成是一个非常完整的一个功能体系
我们也通常说它是一个面向业务场景的一个功能
那每一个微服务其实是专注的一小块功能
那么组装以后就是面向一个完整业务场景
就像我们图里面的我们播放一个稿件
比方我们从web-interface这个入口进来
意味着这个是我们的一个
整个web请求的一个网关入口
那么请求进来以后呢
比方说我们要播放视频
可能会需要打开首页
那么这个里面就会从tag取一些
因为视频页面上有一些标签数据
那么从tag然后从动态
动态指的是最近比较活跃的一些视频
从这些加载一些索引列表以后
它可能会请求稿件的一些信息
请求稿件信息
稿件信息因为它涉及到作者
所以它又请求到账号信息
最终联动了多个微服务以后
它就形成了一个面向业务场景的
然后的这样的一个API
最终可以展现给到我们的用户
这个是单体架构
微服务以后呢
其实微服务它也不是说一蹴而就
立马就出来了
我们以前经常会谈论一个叫SOA
我相信大家都接触过
SOA其实指的是叫面向服务的一个架构模式
那么它和微服务又是一个什么样的关系呢
我通常是这样理解
你可以把微服务想成是
SOA的一种最佳实践
比方我们图里面早期就是一个全家桶
然后单一的一个数据库
也没有划分的那么清楚
这样的话整个单体应用是交给我们整个团队去维护的
大家要考虑非常清晰的模块划分
然后依赖等等
对我们架构其实挑战是非常大的
刚才我们讲了我们要什么
化繁为简
分而治之
所以说它其实就从
我们的单体的这种模型
逐渐演进到分布式的一个微服务的一套模型
那么这个图里面我们其实看到了
它从一个拆成了四个
然后每一个人都拥有自己独立的数据库
这个其实也是微服务非常有意思的一个点
就是微服务的它的隔离性和独立部署
隔离性是非常重要的一个特点
我们既然把微服务想成是SOA的一种最佳实践
那么它背后其实是有一些有一些理论的
那我觉得这四个点是非常重要的
首先我们看一下第一个小即是美
我们刚刚讲的是分而治之
那么小的服务代码量代码总量它相对少
就是我们去开发或者测试的过程中
它的bug其实肯定也因为代码体积变小了
所以它bug也会少
包括我们去定位一个bug也会变得简单
因为我们服务与服务之间通常是通过API交互的
我如果去遇到一个服务本身的bug
我整个去排查这个bug
我的整个收敛只在这个服务里面去排查
相对也会简单一些
对于单个服务来说它是相对来说
是易测试易维护
然后也更容易不断去迭代完善和精致
进而达到一个美妙
为什么
因为你的单个服务你可以不断去优化升级
我在发布的时候能保证平滑的上线
那么其实只要保证我的API是向下兼容的
其实随着时间的一个演进
我可以很好地让我的架构
得到一个不断的完善
因为如果你是个整体的一个单体架构
你每天这样去发
我要去改里面的一小个功能
我要每天这样高频地去发版或者去上线
其实对我们的整个网站的可靠性是很大风险的
因为我们知道运维的工作可能是要控制变更
那么对于研发来说它是要开发更多的功能尽快上线
那么其实它本质上是存在一定冲突的
那么你在单体架构里面
因为每一个小功能都要都要去交付上线的话
发版次数如果明显变多
其实大概的事故也会变高
所以我们经常在单体架构里面
有所谓的固定时间交付日
比方说我们到周三了统一发版
我相信大家也遇到过这种
比方说周三统一发版
每一周的一个变更次数就是
就周三这一个时间
同时周三这个时间
因为它堆积了大量
来自不同业务不同服务的各种变更
所以这一次变更其实是非常大的
那么一旦上线出问题
它的影响面是很大
并且你要定位它是很困难
你只能快速回滚
然后再去查这么多变更里面是哪一个功能出了问题
所以它会变得复杂
但是我们拆分以后
因为每一块服务你的API是向下兼容的
那么我在迭代我这个服务的时候
我只要保证我的API契约是ok的
然后数据逻辑是ok的
它也是能够很快速去交付
那么每一个微服务
它都能够独立的非常满足我们的
所谓的持续集成的这么一个理念
我快速开发快速上线快速验证
你交付的代码比较小
所以你的服务交付速度也会高得多
这个是第一个
那么第二个叫单一职责
我们通常来说
包括我们写
包括大家如果有看过像代码整洁之道这本书
其实就会提到很重要的一个点就是
一个函数通常我们只做一件事情
而甚至我们衍生的更大一点就是一个微服
我们通常只做一个功能
那么这围绕这一个完整的功能场景
我们才能把这个事情专注做好
这是第二个
其实单一职责是在我们的类设计里面
在代码设计里面
包括在微服务设计里面
甚至是架构设计里面
其实单一职责是非常重要的一个点
我们看一下第三个点
就是尽可能早的去创建原型
我们以前的工作逻辑是什么样的
搞清楚一个业务场景以后
然后大家可能去做一些
所谓的代码的一个模型设计
可能是定义一些class或者一些struct
然后把我们的UML那种图
类与类之间的一个关系画出来
但是做了微服务拆分以后
其实从类的关系
我们其实就演进到什么
服务与服务之间的关系
那么它们以前从类的依赖或者类的引用变成了什么
消息的一个交互
它是基于API两个服务之间传递消息来达到一致性
有了微服务以后其实也促使我们
尽可能早的去定义一个服务的API
那么你们在开发过程中
其实有了你的API
我就有办法基于这个API做一些mock
那么对于主干流程开发的人
他其实不会受到一个阻塞
他可以立马去做接下来的一个业务逻辑的开展和开发
对于API的背后的实现和完善
其实可以慢慢去做
这是第三个点
第四个点是什么
叫可移植性比效率更重要
因为服务间的一个轻量级的交互协议
在效率和可移植性这两者之间
我们首要考虑的是兼容性和移植性
为什么这样讲
我们以前做一个完整的功能
可能全部是一种语言
比方全部是Java
那我们随着微服这种理念进来以后
每一块功能可能有它最适合的一种
语言的一种交付方式
举个例子像我们做信息流推荐
比如我们做视频网站
我们要做内容推荐
有可能在我们的首页
可能需要Go服务做一些网关的编程
但是在我们推荐里面可能需要使用像C++
因为它涉及到推理和打分
这些可能对我们的计算密集性要求会非常高
这个时候你用C++的话可能就更加的合适
所以说不同语言应对不同的场景
但是为什么说要考虑兼容性和一致性
因为两种语言
比方说同一个RPC框架
它都是一种模型的话
这样的话整个我觉得会好得多
所以它交付起来
包括兼容性
RPC的兼容性其实是非常重要的
所以服务这样的一个RPC或者是消息队列
一定是用我们通用不是绑定语言的
就我们所谓的language specific
绑定某种语言的话
你其实未来要使用其它的语言
去进行一个通讯的话会变得非常困难
比方说我们如果使用
类似像Java里面固定的一些序列化的方式
如果你要用PHP或者Go或者其它的语言
你再去跟它通信
它不得不再去改造一个
HTTP RESTful的一个接口单独跟你对接
其实对我们开发人员来说
同一个逻辑如果出了两个Endpoint两个接入点
我必须都维护其实是变得会更复杂
所以我们倾向说
一定是选一个兼容性更高的一套框架或体系
来去解决这个问题
我们讲了这么多就是它从
巨石架构到微服务演进有这么多好处
我们其实在这个地方就要做对微服务进行一个定义
到底什么是微服务
我们看一下
简单来说就是
围绕业务功能去创建的
并且服务关注的是单一的一个业务
服务之间采用的是轻量级的通讯机制
并且通讯机制应该我理解是可以相互兼容
各种语言是可以都打通的
标准的HTTP RESTful和各个语言其实都支持
然后也比较好实现
另外它可以独立部署
因为我们看到每一个微服务它都是独立部署的
同时你可以使用不同的编程语言和存储方案
因为我们以前在做的时候
写成一个巨石架构的时候
我们有可能因为在一个代码仓库里面
是很容易可以直接访问到别人的数据库的表
这个时候其实就是绕开了我的
绕开了我的业务逻辑
就是代码的一个模型datasource
而是访问强依赖我底层的一个我们叫对它做datasource
底层的一个对它做被你强依赖了
假设有一天我这个功能要进行一个演进
要进行一个开发
我的数据底层的模型会产生一个变化
有可能几块代码都会变化
几块代码都会变化
我们采用微服务通讯以后
底层的一个使用什么存储其实我们并不关注
因为我只依赖你的API我不管你的数据库
从MySQL切到TiDB或者切到MongoDB
各种各样的存储我其实都不管
因为我只关注我们是兼容的
然后一个轻量交付的一种通讯协议
那么微服务最终就是它的这种架构是
是通过业务拆分来实现叫服务组件化
把一个服务当成一个组件拆分了
那么通过组件之间的组合
完成一个开发系统
这个开发系统我比较喜欢叫
面向业务场景的一个API
或者面向业务场景的一组功能
那么业务单一的服务组件
它可以被独立地部署
使得最终整个系统变得非常灵活
因为你这个服务比方说像我们账号服务
它作为底层的一个模块
它可能会被很多上游的业务去依赖
它可以比方账号服务本身存在一个瓶颈热点
我单独去扩缩容这一块
或者说我这个账号服务有大量的一些需求
我只要单独交付这一个服务
所以使得我们更加的灵活
就像我们右边这张图
一个标准的微服务的模型
这个地方是有移动端的App
还有我们的浏览器
它通过我们的网关
或者是通过我们的WebApp
然后把请求转发到我们的内部的像账号服务
仓库服务或者是我们的Shipping Service就是我们的物流
然后每一个独立的服务都有自己的独占的DB
其实这样还会带来一个好处
每个人既然独占DB意味着我有更多的一个
能力能够让DB部署在不同的物理节点上
其实它的整个吞吐会变得更高
其实按照功能拆封以后
我就从一个DB变成三个DB
那么它整个我们系统的一个所谓的吞吐会变得更好
因为我见过很多巨石架构以前可能cache是一套
然后DB是一套
消息队列可能就一个
它整个在这一个大模型里面去玩
一旦我出现系统的一个瓶颈的时候
我这个DB怎么扩容
我吃了我可能已经占了48G
然后48核
但是这样的话再想去我们所谓的scale up 扩容
就是我去单机扩容的话
它其实会变得非常困难
如果按照微服务拆分的方式
每一个业务单元它有自己独占的DB
如果它存在瓶颈
我只要扩缩容这一个我的这一个DB
去让它的硬件性能变强
或者说它一旦cache有瓶颈
我去增加一些实例
它都能解决问题
所以微服务里面非常重要的几个点
第一是它一定是一个原子服务
原子服务意味着这一个服务关注的是
单一的业务场景单一的一个业务
然后它的API
它的API一定是围绕这一个业务单元来构建的
第二个是它是能够独立部署和交付的
它能够独立部署和交付
然后第三个是隔离
隔离的话其实像我们早期
以前我们B站最早期就是8台物理机
然后它部署在因为它是巨石架构
就是我们所谓的一坨代码
部在这8台物理机上
如果出现一些性能瓶颈要去扩缩容
其实是比较困难的
我现在的话随着后续
就是我们轻量级的东西像Docker
或者像容器的编排引擎
kubernetes这种东西都逐渐
我们叫cloud native
云原生的一些组件越来越活跃
越来越火越来越实用以后
我们其实大概是在2016年
逐渐地从物理机演进到了容器
然后并且我们当时是其实也经过了好几代
最早是使用我们的Docker Swarm
然后逐渐到我们切到我们的K8s
来做我们的容器的隐形编排
那么我们知道每一个独立的微服务
每一个独立的应用
我们其实可以构建一个镜像
这样的话无论是它的部署的交付方式
从镜像然后拉起一个镜像到我们从一个image
然后到拉起到运行是非常方便的
并且因为容器比方说基于我们Linux的CGroup
它可以完成内存CPU
类似CPU它的一些这种隔离
这样的话其实整个隔离性也做得非常好
也就是说我一台大的物理机我可以运行好多组
好多组pod好多组container
好多个容器可以在一台物理机上运行
因为我们通常以前习惯是部署规模体
就是规格比较大的
比方48核48G
我们规格比较大
但是我们其实现在更倾向说
部一个4核4G、4核8G的小的一个容器
随着它的数量变多
因为我数量多意味着
我可以让它部署在越来越多的物理机节点上
我们一般把物理机节点叫一个node
一个节点部在越多的物理机上
那么我当台物理机的故障
对单个服务的影响
它其实会变小
因为我的这一个
比方账号服务
以前是部署在两三台物体机
虽然它能扛住
但是我一旦宕机一台的话
它的影响可能是三分之一的流量
要被剩下的两台机器去完全地承载
但是我现在假设4核4G的一个容器
我布了一二十个这样的容器
它们可能封存在不同的物理机
那么我当台物理机的一个crash
其实对我整个业务的影响会变小
所以这个也是非常重要的一个点
第四个叫去中心化的服务治理
其实又里面包含很多层意思
我们先讲第一层
第一层指的是很多微服务存在热点
那么我们以前比方说
以前我要调某一个微服务
可能是经过一个集中式的Nginx
经过一个类似像四层负载均衡LVS
然后七层负载均衡Nginx
然后再转发到我的应用的业务服务
但是出现这种场景的话
那么我请问你的四层7层
是不是你的流量如果密集的话
这些基础设施也是会容易产生一些流量
就是你的流量一大
那么你上有依赖的这些中间过往的这些通道
它都会变得非常热
那么你需要跟着去扩容
我们尽量去让服务与服务之间的通讯变得更简单
那也就是说直连
也就是说我的A服务和B服务
节点之间是直接RPC直连通讯的
那么尽量减少两个服务之间
这种所谓的集中式的负载均衡
这种中心化的一个治理方式
其实这里面包含很多DB Cache
然后消息队列
甚至是我们的四七层的RPC负载均衡等等
都是一种去中心化的方式来去做
当然了我们有些场景比如像服务发现
因为它可能需要被多个微服务共享
然后来完成一个地址发现的一个功能
这种场景下没办法
你可能就需要一个集中化的服务发现的一个组件
那么我们在第三节讲RPC和服务发现的时候
也会去讲如何让海量的服务能够可以注册
虽然说我们讲了微服务的
它的一些基本的一些定义和概念
但是其实有些微服务它也是有一些缺点的
比方说它对我们基础设施的建设的高度依赖
举个例子
刚刚我们说了你要从一个巨石应用
变成无数个小应用
比方说像我们现在可能有上千个应用要去维护了
那么是不是意味着我的应用的管理的复杂度会变高
比方说我要管理这么多应用归属的人是谁
监控设施和体系
日志查看
因为以前你可能就是四五台机器
然后日志可能就是打到本地的磁盘上
然后你进一台机器tail -f去查看
现在随着我的节点规模变大
我的应用变复杂
我其实还可能需要集中式的日志查看
这些其实都是对我们基建的一个要求
同时实际上它对我们从技术角度来讲
它的复杂度是会变高
因为以前可能我就在一个应用里面
排查问题虽然说它也比较复杂
但是我变成一个分布式架构以后
它的技术的它的可用性的复杂多
还有数据一致性的复杂度
其实也会变得很高
我后面还会举一些比较实际的一些case
像我们做测试的时候
以前测一个功能
可能我们测试小姐姐
只需要围绕你这一个单体应用
去让你交付一个分支
然后构建
然后上线测试
但是现在如果我完成一个复杂功能
比方说像会员功能
它可能联动了七八个服务
都要去同时同步地上线
并且每一个服务都必须保证交付完成
然后你才能去进行一个完整的测试
但是如果中间有一个服务没有构建好
那么我们测试同学可能会认为
是不是有bug
但实际上是你整个构建过程出现了一些问题
这些都是复杂度变高的的一些case
所以微服务它的不足真的是非常多的
所以有一句话叫没有银弹
有一位大佬曾经说过叫没有银弹
所以凡事有利弊
它也不是说万能的
我觉得这个跟我们的
我们公司的一个业务的一个发展
然后体量规模都有关系
比方说我们看到下面这张图
随着你的产品的迭代
业务的增长
我们看看巨石架构
巨石架构 它的其实效率是在逐渐下降的
它变得效率越来越差
我们看微服务它其实下降
虽然肯定也是会下降
因为你业务越来越复杂效率肯定会下降
但微服务它的整个衰减的力度没有那么狠
这个其实是非常重要的一个点
我这里面就要讲个段子
以前我们在像2015年的时候
我们刚开始进行一个
从巨石架构到微服务的演进的时候
当时遇到最多的其实是可用性的一些问题
我觉得早期我们监控体系制度不那么完善的情况下
我们只要网站出一些问题
出现一些抖动
立马就有微博的
我们叫热心人士
它就会在微博上发帖说我们炸了
然后又炸了
然后我们都会关注他
然后订阅他
然后他只要一发消息
我们觉得肯定是要出问题
带来了很多一些可用性的问题
那么它有哪一些弊端
第一就是说
微服务应用它是一个分布式系统
它因此会带来固有的一个复杂性
从1个应用到维护1000个应用
它一定会带来复杂性的变化
那么开发者不得不使用RPC或者消息传递
来实现进程间的通讯
以前我可能是调一个class
我现在是需要进程的通讯
进程间通讯它会带来很多什么网络问题
然后RPC服务不可达
或者是RPC变得很慢
这些问题都是一些局部失效的问题
那么局部失效如果影响到全局
我们所谓的这种的叫连锁故障
因为局部性的一些事故
导致了全局受到影响
所以在微服务引入以后
其实围绕微服务
做了很多可用性的工作是非常多的
这个是需要重点去关注的
那么第二就是说分区的数据库架构
我们以前非常习惯于一个事务
去操作一个很多张表
这个表可能是不同业务单元的
完成一个复杂的数据的一致性的场景
因为它可能是涉及到多个业务主体
这种很普遍
但是在微服务应用架构中
那么如果我们按照不同的业务功能和单元
进行拆分以后
每一个微服务它都自己使用自己独占的一个数据库
独占的一个cache
那么对我们开发人员去解决一个分布式事务
解决数据一致性的一个场景
会变得更复杂
可能你需要依赖消息队列
依赖消息机制
依赖二阶段提交等等
一系列的一些分布式事务的一些手段
包括我们接下来Go训练营里面
也会去专门讲分布式缓存和分布式事务
那么这些其实对我们的代码和架构
其实会变得更复杂
这是它带来一些弊端
第三就是说
测试一个基于微服务架构的应用是很复杂的
比方说我们的测试环境里面有好多种服务和应用
包括我们的集成环境
假设我要做一些日常的回归测试
如果有一个微服务交付不正常
因为你的应用数量在变多
所以每一个都要保证正常的难度在变高
这个也是带来的一个挑战
包括还存在说多个微服务之间
可能基于某一个
就是大家有不同的分支
可能我要去今天测一个A功能的时候
它可能会涉及到
不同微服务之间的不同的分支的发布交付
然后构建一个稳定的环境去让我们测试同学去测试
其实也是非常非常难的一个点
还有就是说服务模块间的依赖
应用的升级有可能会涉及多个模块的一个修改
也就是说我们现在要交付一个复杂的业务场景
因为它之前是由多个微服务它们来compose
它们来协作完成的
那么我一个大功能的一个升级
一定是会涉及到多个服务都去修改的
那么这些需要多个人力同时交付服务的话
本质上从管理角度来说
它也是也是非常复杂的一件事情
还有我们之前提到的
对运维的基础设施的挑战非常大
我们总结来说对运维设施有
像你的这么多应用本身需要管理
你可能需要一个服务树来管理这么多应用
这些服务是做什么的
归属哪个部门哪个业务
谁是开发人员
谁是SRE人员
谁是测试人员
你需要对这个应用本身是有管理
甚至这个应用还有一个名称
有一个唯一的ID
然后你需要对这些应用
能够快速地我们做单元测试也好
做线上测试也好
还有构建镜像也好
去部署的叫测试环境也好
然后因为你的应用数量在变多
我需要有一个可靠的
发布和上线的一个运行时平台
然后这么多应用需要很复杂的一个链路监控
或者是指标采集 日志的一个采集
我们所谓的微服务的可见性
就是指标日志还有链路追踪
这些其实都是会对我们基础设施提出挑战
当然了微服务与微服务之间
可能还需要所谓的微服务的治理框架编排框架
然后还需要分布式缓存分布式事务的一些组件
甚至还有服务与服务之间
它不不仅仅是RPC通讯
还有一个异步通讯
可能要使用消息队列
这些都是对我们中间件会提出很多要求
但我觉得这本质上还是个好事
随着我们的业务变复杂规模变大
那么这些来推动我们的技术的一个演进和发展
这些问题虽然它是有利有弊
但是这些弊端随着我们的技术的一个演进和升级
逐渐可以得到一个解决
我们要去要去实现一个微服务
我们第一个思路是什么
叫组件服务化
我们看到下面这张图
以前所谓的SOA
就是一个虽然也划分了好多种应用
划分了好多应用
然后通过一个所谓的企业总线ESB
我虽然没有用过
但是我以前听听过
据说是一个很复杂的消息的一个系统
然后是因为ESB的引入
然后它也非常重
然后可能还是跟语言绑定的
导致有很长一段时间
我们所谓的SOA被人吐槽的不行
SOA这种面向服务架构这种模型
为什么
因为企业总线太重了
但是我们刚刚说了
微服务我们可以认为是
轻量级的通讯机制的一个SOA的最佳实践
所以其实又慢慢在变好
那么我们要实现一个微服务
刚刚说的提到的是组件服务化
那么它需要什么
我们传统的组件
通常是提供各种各样的代码的库
或者是SDK
那么我们的库和应用是一起在运行进程中的
那么库的局部变化
我们的库的一个局部变化
会意味着整个应用需要重新交付
那么整个应用就是一个巨石架构完整的一个应用
那么通过服务来实现组件以后
因为你把它应用拆分
拆散成一系列的服务
运行在不同的进程中
那么单一服务的局部变化
只需要重新部署对应的服务进程
举个例子我们用Go来实施一个微服务
实际上需要做到哪些事情
第一我们需要有一个微服务的基础库
我们或者叫基础框架
比方说像我们B站开源的Kratos框架
它就是一个kit
我们所谓的kit就是个基础库
比方说基础库里面可能会包含注册中心
然后会包含日志的抽象
会包含像应用生命周期的管理
依赖注入这些一些基础的kit功能
我们叫基础功能
然后第二块是什么
你需要一个service的代码
service代码会包含具体的
围绕单一业务构建的业务代码
然后加上你这一个微服务的基础库的一个依赖
以及你可能需要依赖第三方的一些组件
还有可能你会依赖第三方的一些微服务
可能是API的一些依赖
这样的话它就形成了一个微服务
为什么这里面还提到
它是第三方依赖组成的一个业务微服务
因为你的A这个微服务
它可能从功能层面
需要依赖B和C才可能构建成一个完整的业务单元
所以它也会对第三方微服务可能产生依赖
当然了我们这种层级一般不会太深
比方说A依赖B
A调B B调C C调D
那么这种层级一般来说不会太深
可能也就两三级
第三个就是需要考虑
跨语言跨微服务跨进程的
一个通用的RPC框架和消息队列
我们也叫一个轻量级的通讯机制
我们刚刚讲的
我们一定要考虑的是什么
可一致性和兼容性
这个框架是这些
这些点是非常重要的
我们再强调最后一句话
就是本质上等同于多个微服务compose
就组合完成了一个完整的用户场景
叫usecase
所以是多个Microservice
compose成一个usecase
一个完整的业务场景
所以这个叫什么
叫组件服务化
然后还有一个非常有意思的话题
就是我们微服务到底应该如何去组织
我相信在很多公司一般来说
举个例子像我们一个订单服务和数据访问服务
那么前者反映的是一种
围绕真实的订单相关业务形成的一个团队
可能这是一种组成的一种方式
那么还有一种就是说
我们可能是围绕的是一种技术抽象
服务不反映真实的业务
它只是一种技术的一个抽象
比方说DBA团队
它去维护数据库
如果是按照微服务的一个架构理念来划分的时候
我们通常是什么
叫一个披萨团队
通常是围绕业务闭环来构建团队的
以前可能是按照职责划分团队
就是UI的同事搞前端的搞前端
然后写后端搞后端
运维搞运维
DBA搞DBA
可能是按照职能划分的
那么一旦我要
形成一个所谓的叫Cross Functional Team
我们就是跨多功能的
跨多业务技术的一个团队的话
一定是围绕业务来组建的
那么这个业务团队里面
可能既有前端也有后端
也有运维也有测试
还有可能甚至本身这一个研发人员
既负责交付一个应用
也负责测试保证它的质量
甚至他还开发它的代码
我们所谓的全栈工程师就这么来的
为什么会有这种理念出来
就是所谓的全栈工程师
就是你既交付又开发又测试为什么
我们经常有一句话叫什么
you build it you run it
就是这个代码是你写的
你就应该来对它的全部的生命周期来进行管理
什么叫you build it
就是这个代码是你构建的
you run it就是你构建的服务
那么对于它的测试的要求
对它运维上线并且去定位它
找它问题
理论上你作为代码的一个CodeOwner
你应该是最熟悉的
我们不应该靠
把代码开发完之后的质量验证工作
托交给另外一个团队
他对这个业务可能还不够熟悉
或者对这个代码本身不够熟悉
我们也不应该说把一个代码开发完毕以后
它的交付流程是交给运维的
他可能对你的代码
出现一些线上的问题的时候
你让不熟悉代码的人要快速排查
它回头还是打电话找到你
然后让你去排
给你一些线索让你去查
这个其实我们靠什么兄弟团队来兜底的这种逻辑
既效率很差
同时也不符合所谓的我们叫主人公的这种机制
所以传统的这种测试团队或者是运维团队
他们其实也逐渐从以前的职能团队演进成什么
就是测试团队可能他们逐渐做研发工作
研发工作什么
做提供测试平台
自动测试的一套体系
很方便我去做API测试做回归测试做单元测试
它提供的是一套平台能力
包括运维
以前他可能是你交付完之后
他来帮你部署
他来申请资源
现在很简单我把资源申请做成功
运维可能负责审批
那么线上交付他也负责审批
那么整个交付流程全部交给你
你来构建镜像你来上线
这样的话测试团队也好
运维团队也好
让研发能够叫全生产线去交付
开发测试上线这个其实是非常好的
这个是叫全能的一个团队的构建
其实我们公司其实现在很多小组
基本上都是这样的一个角色
就是我们利用测试和运维之前
暴露出来的一些平台能力
我们整个研发把全生命周期给自己去搞定了
自己开发代码自己测试自己上线
然后自己看监控图然后定位问题
当然oncall可能
oncall的话就比方说值班的话
是我们的SRE人员和我们的研发人员一起去负责的
这个其实是我们组织结构上的很大的不同和变化
我们现在从业务的一个对接的一个模型来讲
我们其实目前就形成了一个大前端团队
大前端团队里面有移动端的一个研发和Web研发
他们的整个team
他们面向的是
对接的人是
主要还是产品
因为我要去最终把这个业务形态给落地
那么移动端或者是Web端
他作为Landing page就落地页
他一定是和业务是强交互和强沟通的
因为你的整个交互逻辑都在端上
那么他们这些这一波同学
再跟我们的网关后端团队来进行对接
因为这样的话沟通成本会变少
以前的话比方说
我按照订单服务什么各种服务
拆分成好多个team以后
我的移动端团队要完成一个功能
它是不是要找好多人去对接
这样的话对接会变复杂
我们现在其实主要是让端上同学
对接一个团队
就是网关团队
它负责所有面向端的API
面向usecase
面向业务场景
那么这些网关同学因为是后端研发
它经常性地跟我们的端团队
我们叫什么Cross Functional的一个team
因为它们经常对接业务
所以说理论上对业务或者对人都非常熟悉
他的对接效率非常高
然后我们网关团队
再和下游的一些业务的平台服务
或者是微服务进行一个RPC的一个对接
因为它们本身都是熟悉后端的
并且大家都熟悉RPC框架
那么基于这种API契约的方式
约定去进行一个对接
也会变得非常简单
当然了除了业务服务还有平台服务
那么后端同学再和什么
我们运维同学提供的像SaaS PaaS的一些能力
包括Software-as-a-Service
和Platform-as-a-service
平台服务和软件服务
比方说我们的cache服务
它就是个SaaS
然后我们的在线运行时
它可能是个平台服务
那么这样就形成了
开发团队对软件生产环境的运行负全部责任
它是负全部责任的
这样它的组织模型
跟我们以前的这种巨石架构的这种
什么DBA是DBA
然后运维是运维开发是开发
然后反正开发完之后从来不知道质量怎么样
然后交付是全部靠测试同学测bug
然后交付上去全靠运维
来帮你搞定什么性能监控报警
这个我觉得其实是不是特别好的一种工作模式
它本质上是按照技术抽象来做划分团队的
并没有按照什么主人翁的这种思路
我们叫Code Owner的这种思路
来去对团队进行一个演进
我们最后再看一下去中心化这个场景
每个服务面临的是业务场景不同
那么可以针对性
选择合适的技术解决方案
所以说它可能会存在多语言
但也是避免过度多元化
因为如果一个团队语言非常之多
那么我们不太容易
去构建统一的kit库就基础库
其实包括我要开发很多套SDK
其实是虽然我们鼓励多样化
但是我们还是尽可能避免太多
比方说我们可能像我们的话可能
就是Go Java和c++相对多一点
其它语言相对少一些
这个成本肯定是非常高的
如果是非常多语言的话
所以整个去中心化其实还有包含几块
第一就是数据去中心化
数据去中心化就是我们微服务的演进
每个人独占实例
独占数据库
独占cache
去一个超大数据库搞定所有
第二叫治理去中心化
我们刚刚讲了
可能七层的负载均衡
或者依赖的一些基础设施
所有流量热点密集的地方
避免一个集中式的东西来处理
做分发或者做流量的一个处理
这种事要避免
另外就是技术去中心化
技术去中心化就是
我们不一定要绑定在绑定到某种语言
比方绑定到Java里面某一个特定的Java的框架
那么未来如果要进行一个演进
使用C++做一些加速
做一些高性能要求的服务你也搞不定
它毕竟是不兼容不轻量
不是兼容可移植的一个通讯机制
所以这个是非常重要的
所以不像传统应用是共享一个数据库缓存
我们再强调一下
一定是从隔离性的角度考虑
这个服务能够独立部署交付
并且它使用的资源
它依赖的资源都是隔离的
都是隔离的
数据库缓存都是隔离的
避免相互干扰
因为所有的中心化的东西
它都容易出连锁故障
这个是要一定要重视的
最后就是我们的所谓的
基础设施的一个自动化
无自动化就不微服务
自动化其实要包含测试部署
单一进程的传统应用
因为被拆分成一系列的多进程的服务后
刚刚说了开发调试
测试监控部署复杂度都会变大
所以一定要有一个非常好用的一个交互平台
像我们的话
像CICD的话
我们这边主要是使用Gitlab
Gitlab做代码托管
然后我们基于Gitlab的Hooks
因为它有很多API可以扩展
基于Gitlab的Hooks加上kubernetes
很方便地去做
我们所谓的叫构建单元测试集成测试
因为我相当于利用Gitlab Hook
收到一个PR收到一个commit
我立马启动一个容器去做一个构建任务
或者做一个单元测试任务
这个是非常方便的
而不需要类似像用Jenkins这种来构建
或者是来做CI的一些pipeline
如果你使用Gitlab的话
基于这种API的Hook
整个体验是会非常好的
第二块就是测试了
测试的话一定要把测试环境的
尤其是多测试环境
我相信大家也有这种痛点
也有像传统的话
搞一套超大的测试环境
所有服务都在里面
但是我要做功能开发的时候
你只有一套测试环境
你不可能做功能开发
部署一个不稳定的微服务上去
因为这种情况你的
这种情况的话
如果你要进行一个
进行一个多功能测试的话
第一多功能测试
同时并行测试你不太方便
因为如果你在稳定测试环境里面
构建一个不稳定的一个微服务的话
可能会影响回归测试
这肯定也是不太好的
所以测试其实是包含测试环境
另外就是说我们单元测试以及API自动化测试
像我们的话使用的是YApi这种
有一个带UI的
能够很方便的去构建API的
自动回归自动测试的一套框架
剩下的话其实还有什么基础设施
就是在线运行时
所以我们现在其实都是基于
我们大概17年就已经16年就开始上容器
我们17年就全部所有在线应用都是Kubernetes
包括围绕Kubernetes
因为你应用上线以后
上到容器以后
它其实整个对ELK这种日志的采集指标的采集
还有链路追踪等等一系列
还包括Control Panel
微服务的一些治理框架
治理的一个页面平台都需要有储备
这样的话你的微服务落地才会比较顺利
最后我们提一下它的可用性和兼容性设计
著名的一种思路叫什么
Design For Failure
我们一定是面向失败
面向故障来去设计我们的架构的
因为你的微服务
它已经从单体变成了一个
分布式的跨网络的通讯的一种方式
那么这种微服架构
它采用的是粗粒度的一种进程间通信
粗粒度指什么呢
举个例子我们以前写了一个API
提供的是一个用户ID查询的这么一个接口
如果你现在要查50个用户
因为你有些界面比方说像评论列表
它可能下面有50个
就50条评论
你可能要把50个人的头像对应的一些信息都捞出来
如果你只是提供了一个细粒度的
查用户的API
这样的话你就是要for循环50次
去调50次RPC
跨50次网络请求拿到这50个用户的数据才返回
这样的高就是很高频的一个
我们叫网络的玩法
其实对我们性能会带来很大的影响
这个时候我们通常在两个服务之间
你比方提供一个ListUsers
提供的是所谓的就是叫GetUser
就是multi get
BatchGetUser就是批量去
一批用户那么它只需要进行一次网络请求
就可以拿到大量的用户数据
这个是非常方便的
因为等等类似这样的原因
其实就引入了额外的复杂性
以前你不一定会这样去考虑
你可能就是在调别人class的时候
就是for循环用一个数组list
然后add
但是现在的话你要考虑到它毕竟是跨网络的
这些性能问题可能带来的类似像网络延迟
你的消息序列化的一些性能负载均衡容错
你是忽略其中任何一点都有可能出现事故
那么忽略任何一点都属于对分布式计算的一个误解
很可能就会出事故
所以可用性包括兼容
兼容性指是API的向下兼容
这两块是在微服务体系里面
非常非常重要的一些点
包括我们比方说
应该是在我们微服务的可能性设计里面
我们就会去完整地介绍我们比方用Go
如何能做到高可用的一些
这种微服务的一些框架的建设
比方说如何做好隔离
超时控制负载均衡限流
降级重试等等
这些都是我们后续都会去逐渐来介绍的
一旦采用了微服务的架构模式
那么刚刚说的兼容性是要非常小心的
因为你的接口一旦发布
意味着有人就会依赖
如果服务提供者变更
可能引发服务消费者的兼容性破坏的话
我们一定要做好什么
就一定时刻谨记
要保障服务契约
就是接口的兼容性
这里面其实有很多思路
就是我的API从v1升级到v2
到底应该怎么去做兼容性设计
那么在工程化这个课程里面
我们也会重点去介绍API的一些design
这里面其实也有过去我们很多一些实践的反思
比方说状态码的处理Error的处理
其实我们也是踩了很多坑
犯了很多错误
希望我们后面的一些总结
能够对你们API设计上有很多帮助
我们一般来说有一句话非常经典
API兼容性的设计里面的一句话
叫发送时要保守接收时要开放
按照伯斯塔尔法则的思想
来设计和实现服务的时候
其实主要是要发送数据要保守
意味着最小化的传送必要的数据
因为你不需要的数据不应该提供
所以我们一般微服务提供API的时候
为什么要提供粗粒度的接口
并且为什么我们要区分接口
其实就是说按需给数
按需给数
那么接收时更开放就意味着
你要最大限度的容忍冗余的数据保证兼容性
就是可能你的接口可能有多个版本
那么可能v1v2v3它有好多种不同类型的数据
都会提交过来走你这一个接口
你需要做很多兼容
甚至参数上的一些容错和兼容你都要考虑
比方说如果有个人传个页码没传
或者传的页码非常大
你要做很多参数的一些校验或者冗余
这个其实是在做接口设计的时候非常重要的
比方我们还有一个点就是说
它的接口就是因为我刚才说的
它存在多个版本的一个情况
你肯定包括我们比方移动端的这种
移动端我们APP的升级
它肯定是有很多业务逻辑的一些变化的
我们一般来说新版本老版本
我们的APP都要能使用
你的返回的时候肯定也是要兼容的
那么请求接受的参数也是要做兼容的
都是需要我们去考虑的
我们第一节课的微服务的概览就介绍完了
我们其实主要是介绍了一下
从巨石架构到一个微服务架构的演进
然后什么是微服务
那么微服务它的一些就是好处
或者是不足缺点
那么如果要搞定这些不足
你要做一些什么样的基础建设
需要做哪些事情
包括我们也引入了一些什么
埋的一些伏笔
比方说可用性设计未来要怎么做
接口API设计要怎么做
这些其实通过一些宏观的对微服务的一些理解
希望你对整个后端的业务的这些编程
有一个基础的概念
那么我们在第二大节课的时候
讲Go的异常处理
包括并发编程以及Go的工程化
其实跟微服务都有一定的关系
或者说我们其实不讲微服务
哪怕你是做业务开发的
这些工程化的知识都会对你有非常大的帮助
我们概览微服务基础的一些知识
基本上就介绍到这里
我们接下来会重点介绍
微服务里面内部的
有一些什么样的角色
你比方说可能有网关ApiGetaway
然后可能有Microservice底层的一些服务
把它们全部讲清楚
然后我们到底上线一个完整的业务单元
我们所谓的一个usecase
那么与其服务之间它们如何去组装的
就是下节课我们要去重点去介绍的