重温最少化集群搭建,我相信很多朋友都已经搭建出来,基于Watch机制也实现了出来,相信也有很多朋友有了自己的实现思路,但是,很多朋友有个疑问,我API和服务分离好了,怎么通过服务中心进行发现呢,这个过程是通过什么来实现的呢,本篇我们就来介绍这个“调用过程”。
本篇干货较多,没有代码,阅读请注意休息!
服务化引入
网站系统随着不断的发展,越来越复杂,架构的变迁也会从MVC—>SOA—>微服务,从简单到复杂,从集中到分布,服务化框架的引入是SOA—>微服务过程必须要解决的问题。面对服务的增多,服务分布的部署,服务与服务之间相互的调用,不得不使用服务化框架去解决,著名的dubbo和spring cloud就是这样产生的。
服务化框架介绍
服务化框架分为两部分:远程调用、注册中心。
-
远程调用:远程调用的传输协议有很多种,可以走http、webservice、tcp等。facebook的thrift、google的grpc、alibaba的dubbo都是世界上主流的rpc框架。其重点在于安全、快速、跨语言。
-
注册中心:用于存放,服务的ip地址和状态信息等。比较好的存放服务信息的方案有:zookeeper、consul、etcd。其重点在于避免单点问题,并且好维护。
服务化框架原理和调用方式
根据上面图,服务化原理可以分为3步:
-
服务端启动并且向注册中心发送服务信息,注册中心收到后会定时监控服务状态(常见心跳检测);
-
客户端需要开始调用服务的时候,首先去注册中心获取服务信息;
-
客户端创建远程调用连接,连接后服务端返回处理信息;
第3步又可以细分,下面说说远程过程调用的原理:目标:客户端怎么调用远程机器上的公开方法:
-
客户端发起调用,将需要调用的服务、方法、参数进行组装;
-
序列化编码组装的消息,这里可以使用json,也可以使用xml,也可以使用protobuf,也可以使用hessian,几种方案的序列化速度还有序列化后占用字节大小都是选择的重要指标,对内笔者建议使用高效的protobuf,它基于TCP/IP二进制进行序列化,体积小,速度快。
-
传输协议,可以使用传统的io阻塞传输,也可以使用高效的nio传输(Netty);
-
服务端收到后进行反序列化,然后进行相应的处理;
-
服务端序列化response信息并且返回;
-
客户端收到response信息并且反序列化;
RESTful和RPC
RESTful : 严格意义上说接口很规范,操作对象即为资源,对资源的四种操作(post、get、put、delete),常见的http api都可以称为Rest接口。
RPC :
我们常说的远程方法调用,就是像调用本地方法一样调用远程方法,通信协议大多采用二进制方式。
Http vs 高性能二进制协议
http相对更规范,更标准,更通用,无论哪种语言都支持http协议。如果你是对外开放API,例如开放平台,外部的编程语言多种多样,你无法拒绝对每种语言的支持,相应的,如果采用http,无疑在你实现SDK之前,支持了所有语言,所以,现在开源中间件,基本最先支持的几个协议都包含RESTful。
RPC协议性能要高的多,例如Protobuf、Thrift、Kyro等,(如果算上序列化)吞吐量大概能达到http的二倍(甚至更高)。响应时间也更为出色。千万不要小看这点性能损耗,公认的,微服务做的比较好的,例如,netflix、阿里,曾经都传出过为了提升性能而合并服务。如果是交付型的项目,性能更为重要,因为你卖给客户往往靠的就是性能上微弱的优势。
RESTful笔者不做实际操作的介绍,程序员们个个都懂。
gRPC介绍
gRPC is a modern, open source, high-performance remote procedure call (RPC) framework that can run anywhere. It enables client and server applications to communicate transparently, and makes it easier to build connected systems.
gRPC是一种可以在任何地方运行的现代、开源、高性能远程过程调用(RPC)框架,它使客户端和服务端应用程序透明地通信,并使构建连接的系统更容易。
gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。
在gRPC里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
基于HTTP/2
HTTP/2 提供了连接多路复用、双向流、服务器推送、请求优先级、首部压缩等机制。可以节省带宽、降低TCP链接次数、节省CPU,帮助移动设备延长电池寿命等。gRPC 的协议设计上使用了HTTP2 现有的语义,请求和响应的数据使用HTTP Body 发送,其他的控制信息则用Header 表示。
IDL使用ProtoBuf
gRPC使用ProtoBuf来定义服务,ProtoBuf是由Google开发的一种数据序列化协议(类似于XML、JSON、hessian)。ProtoBuf能够将数据进行序列化,并广泛应用在数据存储、通信协议等方面。压缩和传输效率高,语法简单,表达力强。
多语言支持(C, C++, Python, PHP, Nodejs, C#, Objective-C、Golang、Java)
gRPC支持多种语言,并能够基于语言自动生成客户端和服务端功能库。目前已提供了C版本grpc、Java版本grpc-java 和 Go版本grpc-go,其它语言的版本正在积极开发中,其中,grpc支持C、C++、Node.js、Python、Ruby、Objective-C、PHP和C#等语言,grpc-java已经支持Android开发。
与thrift,dubbo,motan等比较
使用gRPC的公司:
- Mochi中国
- 阿里OTS
- 腾讯部分部门
- Tensorflow项目中使用了grpc
- CoreOS — Production API for etcd v3 is entirely gRPC. etcd v3的接口全部使用grpc
- Square — replacement for all of their internal RPC. one of the very first adopters and contributors to gRPC.
- ngrok — all 20+ internal services communicate via gRPC 一个内网转发产品
- Netflix
- Yik Yak
- VSCO
- Cockroach
gRPC的优点和缺点:
优点:
-
protobuf二进制消息,性能好/效率高(空间和时间效率都很不错);
-
proto文件生成目标代码,简单易用;
-
序列化反序列化直接对应程序中的数据类,不需要解析后在进行映射(XML,JSON都是这种方式);
-
支持向前兼容(新加字段采用默认值)和向后兼容(忽略新加字段),简化升级;
-
支持多种语言(可以把proto文件看做IDL文件);
-
Netty等一些框架集成;
缺点:
-
GRPC尚未提供连接池,需要自行实现;
-
尚未提供“服务发现”、“负载均衡”机制;
-
因为基于HTTP2,绝大部多数HTTP Server、Nginx都尚不支持,即Nginx不能将GRPC请求作为HTTP请求来负载均衡,而是作为普通的TCP请求。(nginx1.9版本已支持);
-
Protobuf二进制可读性差(貌似提供了Text_Fromat功能,没用过);
-
默认不具备动态特性(可以通过动态定义生成消息类型或者动态编译支持);
protobuf的版本
PB具有三个版本:
1:Google官方版本:https://github.com/google/protobuf/tree/master/csharp(谷歌官方开发、比较晦涩和高大上,主库名字:Google.ProtoBuf.dll)
2:.Net社区版本(一):https://github.com/mgravell/protobuf-net(.Net社区爱好者开发,写法上比较符合.net上的语法习惯,主库名字:protobuf-net.dll)
3:.Net社区版本(二):https://github.com/jskeet/protobuf-csharp-port(据说是由谷歌的.net员工为.net开发,在官方没有出来csharp的时候开发,到发博文时还在维护,主库名字:Google.ProtocolBuffers.dll)
至于选用那个版本,跨平台的需求不大的话,可以用版本二、大的话可以选用一或者三。(本文后续选用二为例)
gRPC服务发现与服务治理的方案
目前gRPC主流分布式方案有这么几种: etcd, zookeeper, consul.
1、集中式LB(Proxy Model)
在服务消费者和服务提供者之间有一个独立的LB,通常是专门的硬件设备如 F5,或者基于软件如 LVS,HAproxy等实现。LB上有所有服务的地址映射表,通常由运维配置注册,当服务消费方调用某个目标服务时,它向LB发起请求,由LB以某种策略,比如轮询(Round-Robin)做负载均衡后将请求转发到目标服务。LB一般具备健康检查能力,能自动摘除不健康的服务实例。 该方案主要问题:
1、单点问题,所有服务调用流量都经过LB,当服务数量和调用量大的时候,LB容易成为瓶颈,且一旦LB发生故障影响整个系统;
2、服务消费方、提供方之间增加了一级,有一定性能开销。
2、进程内LB(Balancing-aware Client)
针对第一个方案的不足,此方案将LB的功能集成到服务消费方进程里,也被称为软负载或者客户端负载方案。服务提供方启动时,首先将服务地址注册到服务注册表,同时定期报心跳到服务注册表以表明服务的存活状态,相当于健康检查,服务消费方要访问某个服务时,它通过内置的LB组件向服务注册表查询,同时缓存并定期刷新目标服务地址列表,然后以某种负载均衡策略选择一个目标服务地址,最后向目标服务发起请求。LB和服务发现能力被分散到每一个服务消费者的进程内部,同时服务消费方和服务提供方之间是直接调用,没有额外开销,性能比较好。该方案主要问题:
1、开发成本,该方案将服务调用方集成到客户端的进程里头,如果有多种不同的语言栈,就要配合开发多种不同的客户端,有一定的研发和维护成本;
2、另外生产环境中,后续如果要对客户库进行升级,势必要求服务调用方修改代码并重新发布,升级较复杂。
3、独立 LB 进程(External Load Balancing Service)
该方案是针对第二种方案的不足而提出的一种折中方案,原理和第二种方案基本类似。
不同之处是将LB和服务发现功能从进程内移出来,变成主机上的一个独立进程。主机上的一个或者多个服务要访问目标服务时,他们都通过同一主机上的独立LB进程做服务发现和负载均衡。该方案也是一种分布式方案没有单点问题,一个LB进程挂了只影响该主机上的服务调用方,服务调用方和LB之间是进程内调用性能好,同时该方案还简化了服务调用方,不需要为不同语言开发客户库,LB的升级不需要服务调用方改代码。
该方案主要问题:部署较复杂,环节多,出错调试排查问题不方便。
服务发现负载均衡实现
gRPC开源组件官方并未直接提供服务注册与发现的功能实现,但其设计文档已提供实现的思路,并在不同语言的gRPC代码API中已提供了命名解析和负载均衡接口供扩展。
其基本实现原理:
1、服务启动后gRPC客户端向命名服务器发出名称解析请求,名称将解析为一个或多个IP地址,每个IP地址标示它是服务器地址还是负载均衡器地址,以及标示要使用那个客户端负载均衡策略或服务配置。
2、客户端实例化负载均衡策略,如果解析返回的地址是负载均衡器地址,则客户端将使用grpclb策略,否则客户端使用服务配置请求的负载均衡策略。
3、负载均衡策略为每个服务器地址创建一个子通道(channel)。
4、当有rpc请求时,负载均衡策略决定那个子通道即grpc服务器将接收请求,当可用服务器为空时客户端的请求将被阻塞。