重温最少化集群搭建,我相信很多朋友都已经搭建出来,基于Watch机制也实现了出来,相信也有很多朋友有了自己的实现思路,但是,很多朋友有个疑问,我API和服务分离好了,怎么通过服务中心进行发现呢,这个过程是通过什么来实现的呢,本篇我们就来介绍这个“调用过程”。
本篇干货较多,没有代码,阅读请注意休息!
 

服务化引入

  网站系统随着不断的发展,越来越复杂,架构的变迁也会从MVC—>SOA—>微服务,从简单到复杂,从集中到分布,服务化框架的引入是SOA—>微服务过程必须要解决的问题。面对服务的增多,服务分布的部署,服务与服务之间相互的调用,不得不使用服务化框架去解决,著名的dubbo和spring cloud就是这样产生的。
 

服务化框架介绍

  服务化框架分为两部分:远程调用、注册中心。
 
  1. 远程调用:远程调用的传输协议有很多种,可以走http、webservice、tcp等。facebook的thrift、google的grpc、alibaba的dubbo都是世界上主流的rpc框架。其重点在于安全、快速、跨语言。
  1. 注册中心:用于存放,服务的ip地址和状态信息等。比较好的存放服务信息的方案有:zookeeper、consul、etcd。其重点在于避免单点问题,并且好维护。
 

服务化框架原理和调用方式

  根据上面图,服务化原理可以分为3步:
  1. 服务端启动并且向注册中心发送服务信息,注册中心收到后会定时监控服务状态(常见心跳检测);
  2. 客户端需要开始调用服务的时候,首先去注册中心获取服务信息;
  3. 客户端创建远程调用连接,连接后服务端返回处理信息;
 
  第3步又可以细分,下面说说远程过程调用的原理:目标:客户端怎么调用远程机器上的公开方法:
 

  服务发现,向注册中心获取服务(这里需要做的有很多:拿到多个服务时需要做负载均衡,同机房过滤、版本过滤、服务路由过滤、统一网关等);

  1. 客户端发起调用,将需要调用的服务、方法、参数进行组装;
  2. 序列化编码组装的消息,这里可以使用json,也可以使用xml,也可以使用protobuf,也可以使用hessian,几种方案的序列化速度还有序列化后占用字节大小都是选择的重要指标,对内笔者建议使用高效的protobuf,它基于TCP/IP二进制进行序列化,体积小,速度快。
  3. 传输协议,可以使用传统的io阻塞传输,也可以使用高效的nio传输(Netty);
  4. 服务端收到后进行反序列化,然后进行相应的处理;
  5. 服务端序列化response信息并且返回;
  6. 客户端收到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的公司:

  • Google
  • 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的优点和缺点:

优点:
  1. protobuf二进制消息,性能好/效率高(空间和时间效率都很不错);
  2. proto文件生成目标代码,简单易用;
  3. 序列化反序列化直接对应程序中的数据类,不需要解析后在进行映射(XML,JSON都是这种方式);
  4. 支持向前兼容(新加字段采用默认值)和向后兼容(忽略新加字段),简化升级;
  5. 支持多种语言(可以把proto文件看做IDL文件);
  6. Netty等一些框架集成;
缺点:
  1. GRPC尚未提供连接池,需要自行实现;
  2. 尚未提供“服务发现”、“负载均衡”机制;
  3. 因为基于HTTP2,绝大部多数HTTP Server、Nginx都尚不支持,即Nginx不能将GRPC请求作为HTTP请求来负载均衡,而是作为普通的TCP请求。(nginx1.9版本已支持);
  4. Protobuf二进制可读性差(貌似提供了Text_Fromat功能,没用过);
  5. 默认不具备动态特性(可以通过动态定义生成消息类型或者动态编译支持);
 

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服务器将接收请求,当可用服务器为空时客户端的请求将被阻塞。

.NET Core微服务之路:基于gRPC服务发现与服务治理的方案的更多相关文章

  1. NET Core微服务之路:自己动手实现Rpc服务框架,基于DotEasy.Rpc服务框架的介绍和集成

    本篇内容属于非实用性(拿来即用)介绍,如对框架设计没兴趣的朋友,请略过. 快一个月没有写博文了,最近忙着两件事;    一:阅读刘墉先生的<说话的魅力>,以一种微妙的,你我大家都会经常遇见 ...

  2. .NET Core微服务之路:文章系列和内容索引汇总 (v0.52)

    微服务架构,对于从事JAVA架构的童鞋来说,早已不是什么新鲜的事儿,他们有鼎鼎大名的Spring Cloud这样的全家桶框架支撑,包含微服务核心组件如 1. Eureka:实现服务注册与发现. 2. ...

  3. .NET Core微服务之路:不断更新中的目录 (v0.43)

    原文:.NET Core微服务之路:不断更新中的目录 (v0.43) 微服务架构,对于从事JAVA架构的童鞋来说,早已不是什么新鲜的事儿,他们有鼎鼎大名的Spring Cloud这样的全家桶框架支撑, ...

  4. .Net Core微服务系列--服务发现

    什么是服务发现 首先我们先思考一个问题,当我们在浏览器中输入一个域名比如baidu.com,然后发生了什么才能让我们访问到百度的网页?简单来说,浏览器会首先从主机的hosts文件中查看是否有baidu ...

  5. PaaS服务之路漫谈(三)

    此文已由作者尧飘海授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Monolithic架构在产品访问量很大的情况下,有可能常会导致整个产品迭代或升级过程不能按预期进行,或者上 ...

  6. 基于CentOS7系统添加自定义脚本服务及参数说明【转】

    概述 centos6如果要添加自定义脚本服务只需要把脚本放到/etc/init.d然后授权后用chkconfig添加后就可以管理了,那么centos7又是怎么添加自定义脚本服务呢? CentOS7添加 ...

  7. NET Core微服务之路:基于Ocelot的API网关Relay实现--RPC篇

    前言 我们都知道,API网关是工作在应用层上网关程序,为何要这样设计呢,而不是将网关程序直接工作在传输层.或者网络层等等更底层的环境呢?让我们先来简单的了解一下TCP/IP的五层模型.     (图片 ...

  8. .NET Core微服务之基于MassTransit实现数据最终一致性(Part 1)

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.预备知识:数据一致性 关于数据一致性的文章,园子里已经有很多了,如果你还不了解,那么可以通过以下的几篇文章去快速地了解了解,有个感性认 ...

  9. NET Core微服务之路:实战SkyWalking+Exceptionless体验生产环境下的追踪系统

    前言 当一个APM或一个日志中心实际部署在生产环境中时,是有点力不从心的. 比如如下场景分析的问题: 从APM上说,知道某个节点出现异常,或延迟过过高,却不能及时知道日志反馈情况,总不可能去相应的节点 ...

随机推荐

  1. swift kvc赋值

    1定义模型属性的时候,如果是对象,通常都是可选的(在需要的时候创建,避免写构造函数,简化代码) 2如果是基本数据类型,不能设置成可选的(运行时获取不到属性),而且要设置初始值,否则KVC会崩溃 3使用 ...

  2. 20165304 2017-2018-2《Java程序设计》学习总结

    20165304 2017-2018-2<Java程序设计>学习总结 一.每周作业及实验报告链接汇总 1.我期望的师生关系 2.20165304学习基础和C语言基础调查 3.linux系统 ...

  3. This iPhone 6s is running iOS 11.3.1 (15E302), which may not be supported by this version of Xcode.

    This iPhone 6s is running iOS 11.3.1 (15E302), which may not be supported by this version of Xcode.

  4. springmvc controller方法返回值

  5. 吴裕雄 python深度学习与实践(7)

    import cv2 import numpy as np img = np.mat(np.zeros((,))) cv2.imshow("test",img) cv2.waitK ...

  6. keil mdk5安装

  7. SQL Server日志文件过大 大日志文件清理方法 不分离数据库

    SQL Server日志文件过大    大日志文件清理方法 ,网上提供了很多分离数据库——〉删除日志文件-〉附加数据库 的方法,此方法风险太大,过程也比较久,有时候也会出现分离不成功的现象.下面的方式 ...

  8. Python设计模式 - 基础 - 七大基本原则

    提倡使用设计模式,主要出发点就是实现代码复用,增加代码的扩展性和可维护性.如何设计出简洁.易懂.灵活.优美的代码结构的确是一门学问,透彻理解并践行如下七大原则通常都能取得基本满意的结果: - 单一职责 ...

  9. centos7源码安装heartbeat可能出现的错误以及解决办法

    问题:ipmilan_command.c: In function 'setup_ipmi_conn':ipmilan_command.c:283:2: error: 'sel_alloc_selec ...

  10. POJ-2533.Longest Ordered Subsequence (LIS模版题)

    本题大意:和LIS一样 本题思路:用dp[ i ]保存前 i 个数中的最长递增序列的长度,则可以得出状态转移方程dp[ i ] = max(dp[ j ] + 1)(j < i) 参考代码: # ...