简单聊聊服务发现(redis, zk,etcd, consul)
什么是服务发现?
服务发现并没有怎样的高深莫测,它的原理再简单不过。只是市面上太多文章将服务发现的难度妖魔化,读者被绕的云里雾里,顿觉自己智商低下不敢高攀。
服务提供者是什么,简单点说就是一个HTTP服务器,提供了API服务,有一个IP端口作为服务地址。服务消费者是什么,它就是一个简单的进程,想要访问服务提供者提供的服务来干一些事情。一个HTTP服务器既可以是服务提供者对外提供服务,也可以是消费者需要别的服务提供者提供的服务,这就是服务依赖,没有你我就不是我自己。复杂的服务甚至有多个服务依赖。
服务发现有三个角色,服务提供者、服务消费者和服务中介。服务中介是联系服务提供者和服务消费者的桥梁。服务提供者将自己提供的服务地址注册到服务中介,服务消费者从服务中介那里查找自己想要的服务的地址,然后享受这个服务。服务中介提供多个服务,每个服务对应多个服务提供者。
服务中介就是一个字典,字典里有很多key/value键值对,key是服务名称,value是服务提供者的地址列表。服务注册就是调用字典的Put方法塞东西,服务查找就是调用字典的Get方法拿东西。
当服务提供者节点挂掉时,要求服务能够及时取消注册,比便及时通知消费者重新获取服务地址。
当服务提供者新加入时,要求服务中介能及时告知服务消费者,你要不要尝试一下新的服务。
Redis作为服务中介
Redis里面有丰富的数据结构,拿来存储服务字典再合适不过了。对每一个服务名称,我们用一个set结构存储服务的IP:Port字符串。如果服务提供者加入,调用sadd命令加入服务地址,如果服务挂掉,调用srem命令移除服务地址。对服务消费者使用smembers指令获取所有服务地址然后在消费进程里随机挑一个,或者使用srandmemember指令直接获取随机服务地址。
这个时候你也许会表示怀疑,服务发现真这么简单么?答案是还差一点,关于上面的这个解决方案有几个问题。
第一个问题是服务提供者进程如果被kill -9暴力杀死,不能主动调用srem命令怎么办?
这个时候服务列表中多了一个黑地址指向了不存在的服务而消费者完全不知道,这个时候服务中介就成了黑中介了。那该怎么办呢?
我们引入服务保活和检查机制,并更换数据结构。服务提供者需要每隔5秒左右向服务中介汇报存活,服务中介将服务地址和汇报时间记录在zset数据结构的value和score中。服务中介需要每隔10秒左右检查zset数据结构,踢掉汇报时间严重落后的服务地址项。这样就可以准实时地保证服务列表中服务地址的有效性。
第二个问题是服务列表变动时如何通知消费者。有两种解决方案。
第一种是轮询,消费者需要每隔几秒查询服务列表是否有改变。如果服务很多,服务列表很大,消费者很多,redis会有一定压力。所以这时候可以引入服务列表的版本号机制,给每个服务提供一个key/value设置服务的版本号,就是在服务列表发生变动时,递增这个版本号。消费者只需要轮询这个版本号的变动即可知道服务列表是否发生了变化。因为服务列表比较稳定,仅在网络严重抖动的情况下才会频繁发生变动,所以redis几乎没有压力。
第二种是采用pubsub。这种方式及时性要明显好于轮询。缺点是每个pubsub都会占用消费者一个线程和一个额外的redis连接。为了减少对线程和连接的浪费,我们使用单个pubsub广播全局版本号的变动。所谓全局版本号就是任意服务列表发生了变动,这个版本号都会递增。接收到版本变动的消费者再去检查各自的依赖服务列表的版本号是否发生了变动。这种全局版本号也可以用于第一种轮询方案。
第三个问题是redis是单点的,如果挂掉了怎么办?
这是个大问题。正是因为这个问题的存在,流行的服务发现系统都是使用分布式数据库zookeeper/etcd/consul等来作为服务中介,它们是分布式的多节点的,挂掉了一个节点没关系,系统仍然可以正常工作。

那如果整个zk集群挂掉会怎样呢?其实每个服务消费者在本地内存里都会存一份当前的服务列表,即使服务中介集群挂掉,也是可以使用当前的服务列表正常工作的。
那redis作为服务中介就真的不靠谱了么?其实还有个redis-sentinel可以消除redis的单点问题,redis-sentinel可以在主节点挂掉的时候,自动升级从节点为主节点。所以拿redis干这件事也是可以的。用redis干服务发现确实非常简单,虽然这种方式非常不流行。
服务提供者不只是HTTP服务
上面提到服务提供者简单来说就是HTTP服务器,其实服务多种多样。可以是数据库服务,可以是RPC服务,可以是UDP服务等等。
如果是MySQL数据库,那如何将MySQL服务注册到服务中介呢?原生的MySQL可没有提供这样功能。一般做法是提供一个Agent代理去注册。这个代理除了将服务地址注册到服务中介外,还需要监控MySQL的健康状况,以便当MySQL宕机时能及时切换到新的MySQL服务地址。一般这个Agent为了节省资源而不止监控一个数据库,它可以同时监控多个数据库,甚至是多种数据库。
服务配置重加载
服务发现一般只是用来注册和查找服务列表这样一个比较单纯的功能。不过现代的服务发现系统还会集成服务配置管理功能。这样可以实现服务配置的实时重加载。原理也很简单,就是对于每一个服务项,服务中介还会存储一个单独的key/value用来存储这个服务的配置信息。当这个配置项在后台被修改时,服务中介会实时通知相关服务器变更配置信息。比如数据库地址变动,业务参数修改等。
服务管理后台
为了便于服务管理,一般服务发现还会提供一个服务管理后台,用于管理人员查看服务集群的状态。如果服务注册和汇报时提供冗余的配置信息,服务管理后台就可以呈现更为详细的服务信息。服务管理后台还可以将所有的服务依赖组织起来,呈现出一颗漂亮的服务依赖树。
服务发现的一个简单实现
小编在闲暇之余基于Redis实现了一个简单的服务发现系统Captain。读者可以去github上下载这个项目进行学习。我除了编写了服务发现的服务器之外,客户端sdk也一块做了开发,可能不太稳定,希望读者体谅,不要用于线上的业务系统。

在Captain这个项目里,我的服务发现服务器将Redis提供的服务做了一层封装,对外提供HTTP API进行服务的注册和查找,没有使用上文提到的pubsub功能。
简单聊聊服务发现(redis, zk,etcd, consul)的更多相关文章
- 简单聊聊服务发现(redis, zk,etcd, consul)(转载)
服务发现并没有怎样的高深莫测,它的原理再简单不过.只是市面上太多文章将服务发现的难度妖魔化,读者被绕的云里雾里,顿觉自己智商低下不敢高攀. 服务提供者是什么,简单点说就是一个HTTP服务器,提供了AP ...
- 服务发现框架选型,Consul还是Zookeeper还是etcd
背景 本文并不介绍服务发现的基本原理.除了一致性算法之外,其他并没有太多高深的算法,网上的资料很容易让大家明白上面是服务发现. 想直接查看结论的同学,请直接跳到文末. 目前,市面上有非常多的服务发现工 ...
- 我是服务的执政官-服务发现和注册工具consul简介
服务发现和注册 我们有了两个服务.服务A的IP地址是192.168.0.1,端口9001,服务B的IP地址192.168.0.2,端口9002.我们的客户端需要调用服务A和服务B,我们只需要在配置文件 ...
- OcelotAPI 简单使用—服务发现、流控
我这人比较懒 直接上配置文件的图 其中serviceName是服务名称, LoadBalancer是负载均衡策略. 对于流控我为了做测试写的1s 限制5次请求. 剩下的看名字就OK了. 要使用服务发现 ...
- 服务发现之consul理论整理_结合Docker+nginx+Tomcat简单部署案例
目录 一.理论概述 服务发现的概念简述 consul简述 二.部署docker+consul+Nginx案例 环境 部署 三.测试 四.总结 一.理论概述 服务发现的概念简述 在以前使用的是,N台机器 ...
- 来,Consul 服务发现入个门(一看就会的那种)
前言 在微服务架构中,对于一个系统,会划分出多个微服务,而且都是独立开发.独立部署,最后聚合在一起形成一个系统提供服务.当服务数量增多时,这些小服务怎么管理?调用方又怎么能确定服务的IP和端口?服务挂 ...
- 基于 Consul 的 Docker Swarm 服务发现
Docker 是一种新型的虚拟化技术,它的目标在于实现轻量级操作系统的虚拟化.相比传统的虚拟化方案,Docker 虚拟化技术有一些很明显的优势:启动容器的速度明显快于传统虚拟化技术,同时创建一台虚拟机 ...
- 基于Docker的Consul集群实现服务发现
服务发现 其实简单说,服务发现就是解耦服务与IP地址之间的硬绑定关系,以典型的集群为例,对于集群来说,是有多个节点的,这些节点对应多个IP(或者同一个IP的不同端口号),集群中不同节点责任是不一样的. ...
- Go | Go 使用 consul 做服务发现
Go 使用 consul 做服务发现 目录 Go 使用 consul 做服务发现 前言 一.目标 二.使用步骤 1. 安装 consul 2. 服务注册 定义接口 具体实现 测试用例 3. 服务发现 ...
随机推荐
- 用 np.logspace() 创建等比数列
np.logspace( start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0, ) Docstring: Return ...
- Acesrc and Travel(2019年杭电多校第八场06+HDU6662+换根dp)
题目链接 传送门 题意 两个绝顶聪明的人在树上玩博弈,规则是轮流选择下一个要到达的点,每达到一个点时,先手和后手分别获得\(a_i,b_i\)(到达这个点时两个人都会获得)的权值,已经经过的点无法再次 ...
- 实验十四 团队项目评审&个人学习总结
实验十四 课程学习总结 项目 内容 这个作业属于哪个课程 (https://www.cnblogs.com/nwnu-daizh/) 这个作业的要求在哪里 (https://www.cnblogs.c ...
- FileZilla 连接站点 编辑配置文件
- VIJOS-P1152 肥猫的游戏
洛谷 P1488 肥猫的游戏 洛谷传送门 JDOJ 1314: VIJOS-P1152 肥猫的游戏 JDOJ传送门 Description 野猫与胖子,合起来简称肥猫,是一个班的同学,他们也都是数学高 ...
- hdu2281&&POJ1320——Pell方程
hdu2281 输入一个 $N$,求最大的 $n$($n \leq N$)和 $x$,使得 $x^2 = \frac{1^2+2^2+...+n^2}{n}$. 分析: 将右边式子的分子求和化简,有: ...
- 【转】编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程
建议56:使用继承ISerializable接口更灵活地控制序列化过程 接口ISerializable的意义在于,如果特性Serializable,以及与其像配套的OnDeserializedAttr ...
- mui或者uni退出app
在安卓上可以使用 //1.1 var backButtonPress = 0; $.back = function(event) { backButtonPress++; if(backButtonP ...
- Io 异常: Invalid number format for port number
报错信息: Caused by: java.sql.SQLException: Io 异常: Invalid number format for port number at oracle.jd ...
- 解决github release下载慢/下载失败的问题
在使用github时,有时作者会在release中提供编译好的程序,以https://github.com/AkikoZ/alfred-web-search-suggest为例,是一个alfred3的 ...