Consul註冊中心
1.理解Consul
Consul 是 HashiCorp 公司推出的開源工具,用於實現分佈式系統的服務發現與配置。與其它分佈式
服務註冊與發現的方案,Consul 的方案更“一站式”,內置了服務註冊與發現框架、分佈一致性協議實現、健康檢查、Key/Value 存儲、多數據中心方案,不再需要依賴其它工具(比如 ZooKeeper 等),使用起來也較爲簡單。
Consul 使用 Go 語言編寫,因此具有天然可移植性(支持Linux、Windows 和 Mac OS);安裝包僅包含一個可執行文件,方便部署,與 Docker 等輕量級容器可無縫配合。
2.Consul特性
- 算法服務發現
- 健康檢查
- Key/Value 存儲多數據
- 中心
- 支持 http 和 dns 協議接口官方
- 提供 web 管理界面
3.Consul角色
client:客戶端,無狀態,將 HTTP 和 DNS 接口請求轉發給局域網內的服務端集羣。
server:服務端,保存配置信息,高可用集羣,每個數據中心的 server 數量推薦爲 3 個或者 5 個。
4.Consul工作原理
服務發現以及註冊
當服務 Producer 啓動時,會將自己的 Ip/host 等信息通過發送請求告知 Consul,Consul 接收到 Producer 的註冊信息後,每隔 10s(默認)會向 Producer 發送一個健康檢查的請求,檢驗 Producer 是否健康。
服務調用
當 Consumer 請求 Product 時,會先從 Consul 中拿到存儲 Product 服務的 IP 和 Port 的臨時表(temp table),從temp table 表中任選一個· Producer 的 IP 和 Port, 然後根據這個 IP 和 Port,發送訪問請求; temp table 表只包含通過了健康檢查的 Producer 信息,並且每隔 10s(默認)更新。
5.Consul安裝
Eureka 其實就是個 Servlet 程序,跑在 Servlet 容器中;Consul 則是用 go 語言編寫的第三方工具需要單獨安裝使用。
下載:訪問 Consul 官網: https://www.consul.io 下載 Consul 的最新版本。
**安裝:**單節點我們在 Windows 安裝,集羣環境在Linux 安裝
單節點
cd 到對應的目錄下,使用 cmd 啓動 Consul
#-dev 表示開發模式運行 ,另外還有-server 表示服務模式運行 consul agent -dev -client=0.0.0.0
爲了方便啓動,也可以在 consul.exe 同級目錄下創建一個腳本來啓動,腳本內容如下:
consul agent -dev -client=0.0.0.0 pause
6.Consul案例
創建項目,創建一個pom父工程
添加依賴
<!-- 繼承 spring-boot-starter-parent 依賴 -->
<!-- 使用繼承方式,實現複用,符合繼承的都可以被使用 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<!--
集中定義依賴組件版本號,但不引入,
在子工程中用到聲明的依賴時,可以不加依賴的版本號,
這樣可以統一管理工程中用到的依賴版本
-->
<properties>
<!-- Spring Cloud Hoxton.SR1 依賴 -->
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<!-- 項目依賴管理 父項目只是聲明依賴,子項目需要寫明需要的依賴(可以省略版本信息) -->
<dependencyManagement>
<dependencies>
<!-- spring cloud 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
服務提供者service-provider
創建項目
添加依賴
<!-- 繼承父依賴 -->
<parent>
<groupId>com.shsxt</groupId>
<artifactId>consul-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- netflix eureka client 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- spring boot actuator 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- spring boot web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok 依賴 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- spring boot test 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
配置文件
spring:
application:
name: service-provider # 應用名稱
# 配置 Consul 註冊中心
cloud:
consul:
# 註冊中心的訪問地址
host: localhost
port: 8500
# 服務提供者信息
discovery:
register: true # 是否需要註冊
instance-id: ${spring.application.name}-01 # 註冊實例 id(必須唯一)
service-name: ${spring.application.name} # 服務名稱
port: ${server.port} # 服務端口
prefer-ip-address: true # 是否使用 ip 地址註冊
ip-address: ${spring.cloud.client.ip-address} # 服務請求 ip
# 端口
server:
port: 8601
編寫服務
User.java
package com.shsxt.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
//@AllArgsConstructor
public class User implements Serializable {
private Integer id;
private String username;
private Integer age;
public User(Integer id, String username, Integer age) {
this.id = id;
this.username = username;
this.age = age;
}
}
UserService.java
/**
* 用戶服務
*/
public interface UserService {
/**
* 查詢用戶列表
*
* @return
*/
List<User> selectUserList();
}
/**
* 用戶服務
*/
@Service
public class UserServiceImpl implements UserService {
/**
* 查詢用戶列表
*
* @return
*/
@Override
public List<User> selectUserList() {
return Arrays.asList(
new User(1, "張三", 20),
new User(2, "李四", 21),
new User(3, "王五", 22)
);
}
}
控制層
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 查詢用戶列表
*
* @return
*/
@GetMapping("/list")
public List<User> selectUserList() {
return userService.selectUserList();
}
}
啓動類
@SpringBootApplication
public class ServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProviderApplication.class, args);
}
}
服務消費者service-consumer
創建項目
添加依賴
<!-- 繼承父依賴 -->
<parent>
<groupId>com.shsxt</groupId>
<artifactId>consul-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- netflix eureka client 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- spring boot actuator 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- spring boot web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok 依賴 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- spring boot test 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
配置文件
spring:
application:
name: service-consumer # 應用名稱
# 配置 Consul 註冊中心
cloud:
consul:
# 註冊中心的訪問地址
host: localhost
port: 8500
# 服務提供者信息
discovery:
register: false # 是否需要註冊
instance-id: ${spring.application.name}-01 # 註冊實例 id(必須唯一)
service-name: ${spring.application.name} # 服務名稱
port: ${server.port} # 服務端口
prefer-ip-address: true # 是否使用 ip 地址註冊
ip-address: ${spring.cloud.client.ip-address} # 服務請求 ip
# 端口
server:
port: 8701
消費服務
package com.shsxt.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
//@AllArgsConstructor
public class User implements Serializable {
private Integer id;
private String username;
private Integer age;
public User(Integer id, String username, Integer age) {
this.id = id;
this.username = username;
this.age = age;
}
}
/**
* 用戶服務
*/
public interface UserService {
/**
* 查詢用戶列表
*
* @return
*/
List<User> selectUserList();
}
/**
* 用戶服務
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private RestTemplate restTemplate;
/**
* 查詢用戶列表
*
* @return
*/
@Override
public List<User> selectUserList() {
// ResponseEntity: 封裝了返回數據
ResponseEntity<List<User>> response = restTemplate.exchange(
"http://service-provider/user/list",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<User>>() {});
return response.getBody();
}
}
控制層
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 用戶管理
*
* @return
*/
@GetMapping("/manage")
public List<User> userManage() {
return userService.selectUserList();
}
}
啓動類
@SpringBootApplication
public class ServiceConsumerApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerApplication.class, args);
}
}
訪問:http://localhost:8701/user/manage
7.Consul集羣
環境準備
服務器 IP | Consul 類型 | Node 節點 |
---|---|---|
192.168.10.101 | server | server-01 |
192.168.10.102 | server | server-02 |
192.168.10.103 | server | server-03 |
192.168.10.1 | client | client-01 |
安裝
安裝 unzip 命令,創建 consul 目錄,將 consul 解壓至指定目錄。
yum -y install unzip # 安裝 unzip 2
mkdir -p /usr/local/consul # 創建 consul 目錄 unzip consul_1.6.2_linux_amd64.zip -d /usr/local/consul/ # 解壓至 consul 目錄
mkdir -p /usr/local/consul/data # 創建 consul 數據目錄
啓動
註冊中心服務端
以server 服務模式運行三臺註冊中心
# node-01
./consul agent -server -bind=192.168.10.101 -client=0.0.0.0 -ui -bootstrapexpect=3 -data-dir=/usr/local/consul/data/ -node=server-01
# node-02
./consul agent -server -bind=192.168.10.102 -client=0.0.0.0 -ui -bootstrapexpect=3 -data-dir=/usr/local/consul/data/ -node=server-02
# node-03
./consul agent -server -bind=192.168.10.103 -client=0.0.0.0 -ui -bootstrapexpect=3 -data-dir=/usr/local/consul/data/ -node=server-03
參數含義如下:
-server :
以服務端身份啓動(註冊中心)
-bind :
表示綁定到哪個 ip
-client :
指定客戶端訪問的 ip,0.0.0.0 表示不限客戶端 ip
-ui :
開啓 web 界面訪問
-bootstrap-expect=3
:表示 server 集羣最低節點數爲 3,低於這個值將工作不正常(注:類似
ZooKeeper一樣,通常集羣數爲奇數方便選舉,Consul 採用的是 Ra 算法)
-data-dir :
表示指定數據的存放目錄(該目錄必須存在,需提前創建好)
-node :
表示節點在 web ui 中顯示的名稱
註冊中心客戶端
consul agent -client=0.0.0.0 -bind=192.168.10.1 -data-dir=D:\Example\consol\data node=client-01
關聯集羣
在 server-02 和 server-03 和 client-01 節點中輸入以下命令建立集羣關係。
/consul join 192.168.10.101
集羣狀態
在任意一臺服務器中輸入以下命令可查看集羣中所有節點信息。
/consul members
訪問: http://192.168.10.101:8500/ 或者 http://192.168.10.102:8500/ 或者 http://192.168.10.103:8