服务发现
服务发现
侧重点在于理解服务发现原理与简单实战操作
背景
微服务与分布式
定义
- 微服务:将一个系统按业务划分成多个子系统,每个子系统都是完整的,可独立运行的,子系统间的交互可通过RPC(HTTP协议、消息队列等等)进行通信
- 分布式:服务是分散部署在不同的机器上,通过RPC/WebService交互
关系
- 抽象版理解
分布式其实是一种手段,即把不同的服务部署于不同的机器(地区),然后这些服务协同作战。而微服务属于分布式的一种(这里其实有异,因为微服务也可以部署于同一服务器,它更强调单一职责,而不关心是否是分布的),他具有更细颗粒度的划分,重在分散能力,使每个模块都独立。分布式重在资源共享与加快计算机计算速度,同时分散压力
-
通俗版理解
分布式是对业务的拆分,可以分为水平拆分和垂直拆分。
比如一个电商项目进行分布式
-
水平拆分
表示层:直接对接用户,一般指系统界面,用户可以进行数据录入,删除等操作
业务层:一系列数据校验,权限校验等等流程
数据层:与数据库交互,进行数据的增删改查
-
垂直拆分
根据业务进行拆分
订单模块: 仅进行与订单相关的业务流程
商品模块: ·····
用户模块:····
······
-
那么微服务呢?其实可以针对垂直拆分的模块进行细化拆分!
不要理解为微服务属于分布式! 这里举例只是区分 分布式 与 微服务

进入正题
意义
刚才简单介绍了分布式与微服务,那么服务发现是为了解决什么问题呢?很明显,如果是传统的服务架构,服务固定运行于一个固定的IP和端口上,如果需要调用服务,则直接利用IP+端口就可以直接访问了,但是在虚拟化与容器化时代,服务部署变得更加简单,而分布式与微服务对服务的切分使服务数量开始增长,服务实例的启动和销毁也开始变得很频繁,所以服务地址可能在不停的动态变更。
调用方视角:怎么找到想调用的服务?找到了ta是不是能正常提供服务?
服务方视角:我是新人,怎么让别人知道我?我要跑路了(挂掉),又怎么让别人不要再来找我?
简单流程
- 服务启动时候进行注册
- 查询已注册服务信息
- 确认服务状态是否健康
服务注册:服务启动 -> 注册中心注册(ip+port) -> 定时反馈自身状态
服务发现:其他用户可通过注册中心查找到相关信息
服务注册
- 提供某个服务的信息注册到公共的注册中心去
自己注册
服务启动:去统一的服务注册中心进行注册登记
服务运行:持续发送心跳,通知注册中心,服务正常
服务停止:去统一的服务注册中心进行注销
服务异常:不发送心跳,被服务注册中心当作异常,然后移除记录
三方注册
服务启动:三方发现有新服务,就去注册中心帮他登记
服务运行:三方进行监控,通过ping亦或其他方式进行健康检查
服务停止/异常:三方发现服务停止,就去注册中心帮他移除记录
三方注册存在的意义在于,如果服务本身发生了故障,但并没有挂掉,仍然发送心跳,会导致流量仍然打到此服务,所以不能仅凭心跳就确认服务存活,此时需要利用三方的探测/监控进行确认
服务发现
- 新增服务/删除服务能够及时被调用者发现
客户端发现

特点
- Client端本身实现负载均衡的功能,先从注册中心获取可用服务列表,然后通过自身负载均衡算法,选择一个最合理的服务提供者进行调用
优点
- 客户端决定负载均衡算法,可以自由定制策略,比如轮询、加权、哈希等
- 扩展灵活
- 去中心化(这也是微服务的目的)
缺点
- 和注册中心强耦合,如果更改注册中心,需要修改代码
- 负载策略修改,需要更新所有客户端的负载均衡
服务端发现

这里负载均衡器是否属于注册中心都可以,但是他一定要和服务信息进行数据交互才能进行请求转发
特点
- 负载均衡器独立于客户端,Client请求负载均衡器,负载均衡器与注册服务信息进行数据交互,进而选择合适的服务供客户端调用
优点
- 客户端无需关心服务列表,以及采用什么负载策略
- 负载策略更新或者注册中心更换,只需要负载均衡器修改即可
缺点
- 每次请求都需要经过注册中心,注册中心压力会比较大
不知道你有没有看出来,通过对比客户端发现与服务端发现两种模式,发现多中心化,去中心化总是会增加更新代价,这与分布式的数据一致性有何关系?
服务发现框架
Zookeeper | Etcd | Consul | |
---|---|---|---|
原生语言 | Java | Go | Go |
算法 | Paxos | Raft | Raft |
多数据中心 | ❌ | ❌ | ✅ |
健康检查 | ❌ | ❌ | ✅ |
http协议 | ✅(复杂) | ✅ | ✅ |
DNS协议 | ✅(复杂) | ❌ | ✅ |
虽然对比起来,Zookeeper、Etcd不占优势,但是一个毕竟是经验丰富的长辈(旧时代的残党(Zookeeper)),一个是满身光环的新人(新时代的船(没错,船指的就是K8s)桨(Etcd)),那么接下来我们就介绍一下Consul(狗头)吧
Consul
无论Etcd、Consul都是用Raft算法保证一致性与高可用!
但是这里对Consul不做过多的原理介绍,主要是玩一下Consul
特点
- 服务发现
- 健康检查
- K/V存储
- 安全通讯
- 多数据中心
端口
端口 | 说明 |
---|---|
TCP/8300 | 8300端口用于服务器节点。客户端通过该端口RPC协议调用服务端节点 |
TCP/UDP/8301 | 8301端口用于单个数据中心所有节点之间的互相通信, 即对LAN池信息的同步。她使得整个数据中心能够自动发现服务器地址,分布式检测节点故障,事件广播(如领导选举事件) |
TCP/UDP/8302 | 8302端口用于单个或多个数据中心之间的服务器节点的信息同步,即对WAN池信息的同步。它针对互联网的高延迟进行了优化,能够实现跨数据中心请求 |
8500 | 8500端口基于HTTP协议,用于API接口或者WEB UI访问 |
8600 | 8600端口作为DNS服务器,它使得我们可以通过节点名查询节点信息 |
部署
不多BB,直接用Docker-Compose玩一下
1/docker拉取consul最新镜像
docker pull consul // 拉取镜像
docker images // 查看镜像

2/docker-compose一键启动
这里一共启动了4个consul,其中consul1 是主节点,consul2、consul3 是子节点。consul4是提供ui服务
version: '3.5'
services:
consul1:
image: consul:latest
container_name: consul1
restart: always
command: agent -server -client=0.0.0.0 -bootstrap-expect=3 -node=consul1
volumes:
- /data/consul/consul1/data:/consul/data
- /data/consul/consul1/config:/consul/config
consul2:
image: consul:latest
container_name: consul2
restart: always
command: agent -server -client=0.0.0.0 -retry-join=consul1 -node=consul2
volumes:
- /data/consul/consul2/data:/consul/data
- /data/consul/consul2/config:/consul/config
depends_on:
- consul1
consul3:
image: consul:latest
container_name: consul3
restart: always
command: agent -server -client=0.0.0.0 -retry-join=consul1 -node=consul3
volumes:
- /data/consul/consul3/data:/consul/data
- /data/consul/consul3/config:/consul/config
depends_on:
- consul1
consul4:
image: consul:latest
container_name: consul4
restart: always
ports:
- 8500:8500
command: agent -client=0.0.0.0 -retry-join=consul1 -ui -node=client1
volumes:
- /data/consul/consul4/data:/consul/data
- /data/consul/consul4/config:/consul/config
docker-compose -f consul_cluster.yaml up -d // 一键启动服务
查看consul节点情况
docker exec -t consul1 consul members
访问consul界面(ip+8500)
3/服务注册
- 启动一个http服务
python -m http.server 8001
-
使用python进行服务注册
# pip install python-consul from pprint import pprint import consul CONSUL_CONFIG = { "consul_host": "xxxxx", "consul_port": "8500" } SERVICE_CONFIG = { "name": "test1", "host": "xxxxx", "port": 8001, } class Consul(object): def __init__(self): # 建立链接 self._consul = consul.Consul(CONSUL_CONFIG["consul_host"], CONSUL_CONFIG["consul_port"]) self.service_name = SERVICE_CONFIG["name"] self.service_host = SERVICE_CONFIG["host"] self.service_port = SERVICE_CONFIG["port"] def register_service(self, tags=[]): # 注册服务 self._consul.agent.service.register(self.service_name, self.service_name, self.service_host, self.service_port, tags, check=consul.Check().tcp(self.service_host, self.service_port, "10s", "30s", "30s")) # tcp 后三个参数为 检查时间间隔,超时时间, 注销时间 def get_service(self): services = self._consul.agent.services() service = services.get(self.service_name) if not service: return None, None addr = f"{service['Address']}-{service['Port']}" return service, addr def run(self): self.register_service() check = consul.Check().tcp(self.service_host, self.service_port, "10s", "30s", "30s") pprint(check) res = self.get_service() pprint(res) if __name__ == '__main__': Consul().run()
-
查看注册情况
-
关闭服务(时间过后自动注销)
4/服务发现
暂时不涉及访问控制,即不用验证即可访问节点
思考
服务发现中无论是客户端发现还是服务端发现模式,感觉都和DNS的运作原理有点相似,搜索看了一下,阿里巴巴已经有基于DNS的服务发现