服務註冊發現、配置中心集一體的 Spring Cloud Consul
- 2019 年 10 月 3 日
- 筆記
前面講了 Eureka 和 Spring Cloud Config,今天介紹一個全能選手 「Consul」。它是 HashiCorp 公司推出,用於提供服務發現和服務配置的工具。用 go 語言開發,具有很好的可移植性。被 Spring Cloud 納入其中,Eureka 停止新版本開發,更多的想讓開發者使用 Consul 來作為服務註冊發現使用。
Consul 提供的功能包括如下幾個:
服務發現
Consul 讓服務註冊和服務發現(通過 DNS 和 HTTP 接口)更加簡單,甚至對於外部服務(例如SaaS)註冊也一樣。
故障檢測
通過健康檢查,服務發現可以防止請求被路由到不健康的主機,並且可以使服務容易斷開(不再提供服務)。
多數據中心
Consul 不需要複雜的配置即可簡便的擴展到多個數據中心,查找其它數據中心的服務或者只請求當前數據中心的服務。
鍵值存儲
靈活的鍵值存儲,提供動態配置、特徵標記、協作、leader 選舉等功能,通過長輪詢實現配置改變的即時通知。
Spring Cloud Consul 將 Consul 進行自動配置和進一步封裝。
Spring Cloud Consul 可替代已有的 Spring Cloud Eureka,也就是當做服務註冊發現框架使用。並且 Eureka 2.x 版本也已經停止開發,並且 Spring Cloud 官方也建議用 Spring Cloud Consul 來替代,當然如果已經用了 Eureka 在項目中也沒有關係,Eureka 已經足夠穩定,正常使用沒有任何問題。
Spring Cloud Consul 可替代已有的 Spring Cloud Config ,也就是當做配置中心使用。
Spring Cloud Consul 主要用作服務註冊發現,並且官方建議替代 Eureka,那麼它肯定具有 Eureka 或其他框架不具備的優勢,下面看一下對比它和其他服務發現方式做的一下對比(摘自網絡):
功能點 | euerka | Consul | zookeeper | etcd |
---|---|---|---|---|
服務健康檢查 | 可配支持 | 服務狀態,內存,硬盤等 | (弱)長連接,keepalive | 連接心跳 |
多數據中心 | — | 支持 | — | — |
kv 存儲服務 | — | 支持 | 支持 | 支持 |
一致性 | — | raft | paxos | raft |
cap | ap(高可用、分區容錯) | ca(數據一致、高可用) | cp | cp |
使用接口(多語言能力) | http(sidecar) | 支持 http 和 dns | 客戶端 | http/grpc |
watch 支持 | 支持 long polling/大部分增量 | 全量/支持long polling | 支持 | 支持 long polling |
自身監控 | metrics | metrics | — | metrics |
安全 | — | acl /https | acl | https 支持(弱) |
spring cloud 集成 | 已支持 | 已支持 | 已支持 | 已支持 |
Consul 採用 raft 算法來保證數據的強一致性,如此帶來的優勢很明顯,相應的也帶來了一些犧牲:
- 服務註冊相比 Eureka 會稍慢一些。因為 Consul 的 raft 協議要求必須過半數的節點都寫入成功才認為註冊成功;
- Leader掛掉時,重新選舉期間整個 consul 不可用,以此保證了強一致性但犧牲了可用性。
Consul 的安裝和啟動
與 Eureka 不同,Consul 需要獨立安裝,可以到官網(https://www.consul.io/downloads.html)下載。具體操作系統的安裝方式不同,可參考官網。
Consul 提供了一系列的參數,用於在命令行執行。Consul 默認提供了 web UI 界面來查看配置。通過訪問 server 的 8500 端口可以訪問到 web UI 控制台。
開發過程中,我們可以通過命令 consul agent -dev
來啟動開發模式,啟動成功後,訪問 localhost:8500 可以看到當前 consul 的所有服務。如下圖:
更多的在生成環境的部署可自行搜索相關介紹,這裡暫時只用 dev 模式啟動,用來介紹 Spring Cloud Consul 的使用。
實現服務提供者
1、引用 spring-cloud-consul
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-all</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-consul-dependencies</artifactId> <version>2.1.0.M2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
2、設置 consul 相關配置,在 bootstrap.yml 配置文件中,配置如下:
spring: cloud: consul: discovery: service-name: consul-provider ## 服務提供者名稱 host: localhost ## consul 所在服務地址 port: 8500 ## consul 端口
3、設置 server 相關配置,在 application.yml 配置文件中,配置如下:
spring: application: name: consul-provider server: port: 5000 endpoints: health: sensitive: false restart: enabled: true shutdown: enabled: true management: security: enabled: false
4、增加一個 RestController ,寫兩個測試服務方法
@RestController @Slf4j public class HelloController { @Autowired private DiscoveryClient discoveryClient; @GetMapping(value = "test") public String test(){ List<String> services = discoveryClient.getServices(); for(String s : services){ log.info(s); } return "hello spring cloud!"; } @GetMapping(value = "nice") public String nice(){ List<String> services = discoveryClient.getServices(); for(String s : services){ log.info("gogogo" + s); } return "nice to meet you!"; } }
5、Spring boot 啟動類
@SpringBootApplication @EnableDiscoveryClient public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
@EnableDiscoveryClient 註解標示這是一個 client 端。
啟動這個服務提供者,打開 http://localhost:8500 可以看到這個服務
實現服務消費者
1、引用相關 maven 包,除了引用與上面服務提供者相同的包外,還引用了 openFeign
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
2、bootstrap.yml 配置,因為作為服務消費者,所以設置不註冊到 consul
spring: cloud: consul: discovery: register: false
3、application.yml 配置
spring: application: name: consul-customer server: port: 5001 endpoints: health: sensitive: false restart: enabled: true shutdown: enabled: true management: security: enabled: false
4、項目啟動類
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class Application { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
使用 @EnableDiscoveryClient 註解表示作為服務 client 端,@EnableFeignClients 啟用 openFeign 。
5、新建一個 openFeign 服務接口
@FeignClient(value = "consul-provider") public interface IHelloService { @RequestMapping(value = "/hello") String hello(); @RequestMapping(value = "nice") String nice(); }
對應服務提供者中提供的兩個 RESTful 接口地址
6、實現一個 RestController 來訪問服務提供者開放出來的服務
@RestController public class ConsumerController { @Autowired private LoadBalancerClient loadBalancer; @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @Autowired private IHelloService helloService; private final static String SERVICE_NAME = "consul-provider"; /** * 使用普通的 RestTemplate 方法訪問服務 * * @return */ @GetMapping(value = "test") public Object test() { String result = restTemplate.getForObject("http://"+SERVICE_NAME + "/test", String.class); System.out.println(result); return result; } /** * 使用 openFeign 方式訪問服務 * * @return */ @GetMapping(value = "feign") public Object feign() { String s = helloService.nice(); return s; } /** * 獲取所有服務實例 * * @return */ @GetMapping(value = "/services") public Object services() { return discoveryClient.getInstances(SERVICE_NAME); } /** * 從所有服務中選擇一個服務(輪詢) */ @GetMapping(value = "/choose") public Object choose() { return loadBalancer.choose(SERVICE_NAME).getUri().toString(); } }
啟動消費者程序,然後訪問對應的 RESTful 接口,可以得到對應的結果。
實現高可用服務提供者
線上的微服務最好不要是單點形式,接下來通過配置來啟動兩個服務提供者,只要保證 service-name 相同,就表示這是同一個服務。
1、 bootstrap.yml 配置不變
spring: cloud: consul: discovery: service-name: consul-provider host: localhost port: 8500
2、application.yml 修改為如下配置
spring: profiles: active: consul-provider1 endpoints: health: sensitive: false restart: enabled: true shutdown: enabled: true management: security: enabled: false --- spring: profiles: consul-provider1 application: name: consul-provider1 server: port: 5000 --- spring: profiles: consul-provider2 application: name: consul-provider2 server: port: 5002
3、之後啟動的時候加上 vm 參數。分別加上參數:
-Dspring.profiles.active=consul-provider1
-Dspring.profiles.active=consul-provider2
分別在 5000 端口和 5002 端口啟動服務提供者 consul-provider
4、最後仍然訪問消費者的 RESTful 接口地址,可以在服務提供者後台看到每次請求調用的服務實例。
用作配置中心
我們知道,Spring Cloud Config 提供了配置中心的功能,但是需要配合 git、svn 或外部存儲(例如各種數據庫),那麼既然使用了 Consul ,就可以使用 Consul 提供的配置中心功能,並且不需要額外的 git 、svn、數據庫等配合使用。
接下來,簡單介紹一下 Spring Cloud Consul 如何用作配置中心。Consul 支持 yaml 和 properties 格式的配置文件內容,本例中以 yaml 格式為例。
1、引用相關的 maven 包
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
2、bootstrap.yml 配置,這裡主要設置有關 config 的參數
spring: cloud: consul: config: enabled: true # 啟用配置中心 format: yaml # 指定配置格式為 yaml data-key: mysql_config # 也就是 consul 中 key/value 中的 key prefix: config # 可以理解為配置文件所在的最外層目錄 defaultContext: consul-config # 可以理解為 mysql_config 的上級目錄 discovery: register: false
對應到 consul 上,key/value 里的配置如下:
3、application.yml 配置文件內容
spring: application: name: consul-config server: port: 5008 endpoints: health: sensitive: false restart: enabled: true shutdown: enabled: true management: security: enabled: false
4、定義配置文件實體類,指定 @ConfigurationProperties 註解,指定前綴為 mysql,也就是 key/value 配置文件中的頂層 key。
@Component @ConfigurationProperties(prefix = "mysql") public class MySqlComplexConfig { public static class UserInfo{ private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "UserInfo{" + "username='" + username + ''' + ", password='" + password + ''' + '}'; } } private String host; private UserInfo user; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public UserInfo getUser() { return user; } public void setUser(UserInfo user) { this.user = user; } }
5、新建一個 RestController 來獲取輸出 value 內容
@RestController @Slf4j public class ConfigController { @Autowired private MySqlConfig mySqlConfig; @Autowired private MySqlComplexConfig mySqlComplexConfig; @GetMapping(value = "mysqlhost") public String getMysqlHost(){ return mySqlConfig.getHost(); } @GetMapping(value = "mysqluser") public String getMysqlUser(){ log.info(mySqlComplexConfig.getHost()); MySqlComplexConfig.UserInfo userInfo = mySqlComplexConfig.getUser(); return userInfo.toString(); } }
6、最後,啟動應用,訪問 RestController 中的 RESTful 接口即可看到配置文件內容。
與 Spring Cloud Config 相比,Consul 在控制台修改配置後,會立即更新,不用再結合 Spring Cloud Bus 之類的配合了。
源碼地址
如果你覺得寫的還可以的話,請點個「推薦」吧
歡迎關注,不定期更新本系列和其他文章
古時的風箏
,進入公眾號可以加入交流群