一篇讲解“服务调用”的良心之作( 三 )


Nginx 应该是现在用的最多的 的效果 。
如:
upstream hello { server A:11001; server B:11001; } location / { root html; index index.html index.htm; proxy_pass 的配置修改都需要人工干预,效率会很慢 。
这时候就要提到一个叫 DevOps 的名词了,我的理解就是开发各种便于自动化运维工具的工程师 。
有了上面的分析,此时一个提供七层 HTTP 访问接口的服务架构大体是这样的:
服务发现与 RPC
前面已经解决单机服务器对外提供服务的大部分问题,来简单回顾:
域名系统解决了需要记住复杂的数字 IP 地址的问题 。PB 类软件库的出现解决协议定义解析的痛点 。网关类组件解决客户端接入以及服务器横向扩展等一系列问题 。然而一个服务,通常并不见得只由本身提供服务就可以,服务过程中可能还涉及到查询其他服务的流程,常见的如数据类服务如 MySQL、Redis 等 。
这一类供服务内调用查询的服务被称为内部的服务,通常并不直接暴露到外网去 。
面向公网的服务,一般都是以域名的形式提供给外部调用者,然而对于服务内部之间的互相调用,域名形式还不够,其原因在于:
DNS 服务发现的粒度太粗,只能到 IP 地址级别,而服务的端口还需要用户自己维护 。对于服务的健康状况的检查,DNS 的检查还不够,需要运维的参与 。DNS 对于服务状态的收集很欠缺,而服务状态最终应该是反过来影响服务被调用情况的 。DNS 的变更需要人工的参与,不够智能以及自动化 。综上,内网间的服务调用,通常而言会自己实现一套“服务发现”类的系统,其包括以下几个组件:
服务发现系统:用于提供服务的寻址、注册能力,以及对服务状态进行统计汇总,根据服务情况更改服务的调用情况 。比如,某个服务实例的响应慢了,此时分配给该实例的流量响应的就会少一些 。
而由于这个系统能提供服务的寻址能力,所以一些寻址策略就可以在这里做,比如灰度某些特定的流量只能到某些特定的实例上,比如可以配置每个实例的流量权重等 。
一套与该服务系统搭配使用的 RPC 库 。RPC 库提供以下功能:
服务提供方:使用 RPC 库注册自己的服务到服务发现系统,另外上报自己的服务情况 。服务调用方:使用 RPC 库进行服务寻址,实时从服务发现系统那边获取最新的服务调度策略 。提供协议的序列化、反序列化功能,负载均衡的调用策略、熔断限流等安全访问策略,这部分对于服务的提供方以及调用方都适用 。
有了这套服务发现系统以及搭配使用的 RPC 库之后,来看看现在的服务调用是什么样的:
写业务逻辑的,再也不用关注服务地址、协议解析、服务调度、自身服务情况上报等等与业务逻辑本身并没有太多关系的工作,专注于业务逻辑即可 。服务发现系统一般还有与之搭配的管理后台界面,可以通过这里对服务的策略进行修改查看等操作 。对应的还会有服务监控系统,对应的这是一台实时采集服务数据进行计算的系统,有了这套系统服务质量如何一目了然 。服务健康状态的检查完全自动化,在状况不好的时候对服务进行降级处理,人工干预变少,更加智能以及自动化 。现在服务的架构又演进成了这样:
Service Mesh
架构发展到上面的程度,实际上已经能够解决大部分的问题了 。这两年又出现了一个很火的概念:Service Mesh,中文翻译为“服务网格”,来看看它又能解决什么问题 。
前面的服务发现系统中,需要一个与之配套的 RPC 库,然而这又会有如下的问题:
如果需要支持多语言,该怎么做?每个语言实现一个对应的 RPC 库吗?库的升级很麻烦,比如 RPC 库本身出了安全漏洞,比如需要升级版本,一般推动业务方去做这个升级是很难的,尤其是系统做大了之后 。可以看到,由于 RPC 库是嵌入到进程之中的组件,所以以上问题很麻烦,于是就想出了一个办法:将原先的一个进程拆分成两个进程 。
如下图所示:
在服务 Mesh 化之前,服务调用方实例通过自己内部的 RPC 库来与服务提供方实例进行通信 。
在服务 Mesh 化之后,会与服务调用方同机部署一个 Local Proxy 也就是 Service Mesh 的 Proxy 。
此时服务调用的流量会先走到这个 Proxy,再由它完成原先 RPC 库响应的工作 。
至于如何实现这个流量的劫持,答案是采用 Iptables,将特定端口的流量转发到 Proxy 上面即可 。
有了这一层的分拆,将业务服务与负责 RPC 库作用的 Proxy 分开来,上面的两个痛点问题就变成了对每台物理机上面的 Mesh Proxy 的升级维护问题 。

推荐阅读