服务注册与发现
Consul是微服务的一个框架,是服务注册与发现的一个解决方案。在说Consul之前先来看看什么是服务注册与发现
。
微服务体系中,服务注册与服务发现是两个最核心的模块。服务A调用服务B时,需要通过服务发现模块找到服务B的IP和端口列表
,而服务B的实例在启动时需要把提供服务的IP和端口注册到服务注册中心。一个典型的结构如下图:
也即是说,ServiceA需要通过查找ServiceRegistry来“发现”
ServiceB,而ServiceB则需要通过每次启动时向ServiceRegistry发送自己服务的ip+port
来“注册”
ServiceB。
服务注册
目前,流行的注册中心比较多,常见的有zookeeper
、ectd
、consul
、eureka
等。
服务注册通常有三种:自注册
、第三方注册
、注册中心主动同步
。
- 自注册
自注册,顾名思义,就是服务提供方
在启动服务时自己将提供服务的IP和端口发送到注册中心,并通过心跳方式
维持健康状态;服务下线时,自己将相应的数据删除。典型的比如使用eureka
客户端发布微服务。 - 第三方注册
第三方注册是指,存在一个第三方的系统负责在服务启动或停止时向注册中心增加或删除服务数据。典型的用法是devops
系统或容器调度系统主动调注册中心接口注册服务。 - 注册中心主动同步
与第三方注册方式类似,主动注册方式是指注册中心和调度或发布系统打通,主动同步最新的服务IP列表;一个例子是kubernetes
体系中,coredns
订阅api server
数据。
服务发现
在真正发起服务调用前,调用方需要从注册中心拿到相应服务可用的IP和端口列表
,即服务发现
。服务发现从对应用的侵入性上可以分为两大类:
- SDK-Based
这类的服务发现方式,需要调用方依赖相应的SDK,显式调用SDK代码才可以实现服务调用,即对业务有侵入性,典型例子如eureka
、zookeeper
等。 - DNS-Based
DNS本身是一种域名解析系统,可以满足简单的服务发现场景,如双方约定好端口、序列化协议等等。但是,这远远不能满足真正的微服务场景需求。近几年,基于DNS的服务发现被业界提了出来。
服务发现交互协议
微服务架构中,服务注册与发现的通信协议大致可以分为两类:
- 一类是
“私有”协议
,如dubbo
+zk
及eureka
; - 另一类是通用的
DNS协议
,如k8s
+coredns
。
- “私有”协议
“私有”协议具有很高的定制性,可以和具体产品一起实现更高级的功能,如zk + dubbo,可以支持推送和长连接。但是,“私有”协议也带来了另外一个问题,就是开放性很差,第三方接入需要使用特定的SDK,跨语言特性不好。而在微服务或云环境下,跨语言服务注册和发现是非常常见的一个场景。
- DNS协议
DNS协议是一个“古老”的协议,也是最基本、最通用的协议之一。基于DNS协议的服务发现具备很好的通用性,几乎所有语言都可以无缝接入。与“私有”协议的服务发现相比,基于DNS协议的服务发现还有一些问题需要解决,如端口问题、语言框架的缓存问题。
Consul
Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案相比,Consul的方案更“一站式”,内置了服务注册与发现框架
、分布一致性协议实现
、健康检查
、Key/Value存储
、多数据中心方案
,不再需要依赖其他工具(比如ZooKeeper等),使用起来也较为简单。
Consul使用Go语言编写,因此具有天然可移植性(支持Linux
、Windows
和Mac OS X
);安装包仅包含一个可执行文件,方便部署,与Docker
等轻量级容器可无缝配合。
Consul 安装
访问Consul官网,根据操作系统类型,选择下载Consul的最新版本。下载地址为https://www.consul.io/downloads.html。
下载下来是一个zip
压缩包,解压之后,是一个exe
可执行文件。
Consul启动
终端进入consul
可执行性文件所在文件夹,然后执行下面命令即可启动:
consul agent -dev
启动过程信息如下:
[superfarr@19:20:59]:~->cd /Users/superfarr/Documents/iCollections/casaba/consul[superfarr@19:21:08]:~/Documents/iCollections/casaba/consul->consul agent -dev
==> Starting Consul agent...
Version: '1.8.2'
Node ID: 'f011f480-5d47-e343-c26a-6bbd32675721'
Node name: 'superfarrdeMacBook-Pro.local'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false
==> Log data will now stream in as it occurs:
2020-10-11T19:21:18.633+0800 [DEBUG] agent: Using random ID as node ID: id=f011f480-5d47-e343-c26a-6bbd32675721
2020-10-11T19:21:18.638+0800 [WARN] agent: Node name will not be discoverable via DNS due to invalid characters. Valid characters include all alpha-numerics and dashes.: node_name=superfarrdeMacBook-Pro.local
2020-10-11T19:21:18.650+0800 [INFO] agent.server.raft: initial configuration: index=1 servers="[{Suffrage:Voter ID:f011f480-5d47-e343-c26a-6bbd32675721 Address:127.0.0.1:8300}]"
...
启动成功之后,访问http://localhost:8500,如果可以看到如图下图所示的Consul服务管理界面,就说明注册中心服务端可以正常提供服务了。
项目实例:SpringBoot 配置服务注册与发现
-
新建Maven项目
serviceA
-
添加依赖
在pom.xml
中添加Spring Cloud
、Consul
注册中心及Spring Boot相关依赖。
- 注:Spring Boot 2.1后的版本会出现Consul服务注册上的问题,可能是因为配置变更或者支持方式发生改变,所以这里把SpringBoot版本调整为
2.0.4
,Spring Cloud版本使用Finchley.RELEASE
。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hory</groupId>
<artifactId>service</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- spring-boot-admin -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui</artifactId>
<version>2.0.4</version>
</dependency>
<!--consul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
</dependencies>
<!--spring cloud-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 添加配置文件
在resources
下新建application.yml
配置文件,添加服务注册配置:
server:
port: 8000
spring:
application:
name: serviceA
cloud:
consul:
host: localhost
port: 8500
discovery:
serviceName: ${
spring.application.name} # 注册到consul的服务名称
- 启动类
在java目录下新建包com.hory.serviceA
,在包中新建一个启动类ServiceApplication
:
添加@EnableDiscoveryClient
注解,开启服务发现支持。
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @Author Hory
* @Date 2020/10/11
*/
@EnableAdminServer
@EnableDiscoveryClient //开启服务发现
@SpringBootApplication
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
测试
启动服务监控服务器,访问http://localhost:8500,发现服务已经成功注册到注册中心。
测试完之后,不要忘记将Consul关掉(终端Ctrl+C
关闭Consul)。