Spring-Boot:Spring Cloud構建微服務架構
概述:
從上一篇博客《Spring-boot:5分鐘整合Dubbo構建分布式服務》 過度到Spring Cloud,我們將開始學習如何使用Spring Cloud 來搭建微服務。繼續采用上一篇博客中所使用到的圖:
我們先來觀察一下Spring Cloud 的組成,從上圖中可以發現,Spring Cloud 的服務會比Dubbo 完善太多,Spring Cloud 包括了配置管理、服務發現、斷路器、智能路由、微代理、控制總線、全局鎖、決策競選、分布式會話和集群狀態管理等一系列的服務。在後續,我們會對每一個服務進行深入研究,而在本文中,我們將快速入門Spring Cloud ,一起來看看 與Dubbo 相比,Spring Cloud 有何不同。
一、服務治理
1.1、概括
服務治理,或者我們可以理解為服務註冊中心,Spring Cloud 應用中可以支持多種不同的服務治理框架,如:Netflix Eureka、Consul、Zookeeper。
如上圖所示,我們在使用 IDEA 進行 Spring Cloud 項目創建的時候,編輯器會提示我們選擇默認則服務治理框架。
與Dubbo 相比,Spring-Cloud 提供了更加豐富的選擇,而在本次學習博文中,我們將會采用 Eureka 作為服務註冊中心進行開發。
Spring Cloud Eureka 是Spring Cloud Netflix
1.2、創建“服務註冊中心”
項目創建方式,可以參考《Spring-Boot:6分鐘掌握SpringBoot開發》 學習如何快速搭建Spring-Boot 項目,這裏只做簡單的介紹:
1.3、Pom.xml
與Dubbo 不一樣,Dubbo 采用Zookeeper 作為服務註冊中心,因此只需要開發 Productor 和Consumer 即可,而Spring-cloud 中,需要我們獨自開發一個服務治理服務(服務註冊中心),但是由於Spring-boot 帶來的敏捷開發,我們只需要引入:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency>
其他相關依賴,則跟Spring-Boot Web 應用類似,在這裏就不細述了。
1.4、Application.properties
在默認設置下,該服務註冊中心也會將自己作為客戶端來嘗試註冊它自己,所以我們需要禁用它的客戶端註冊行為,只需要在 application.properties
配置文件中增加如下信息:
這裏我們將服務的端口號修改為 8761 ,服務名稱為 eureka-server
spring.application.name=eureka-server
server.port=8761
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
1.5、Application.java
區別於普通的Spring Boot 項目,我們需要為啟動類添加一個 @EnableEurekaServer 註解,用於標識出這個服務是 Eureka 服務:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class SpringBootCloudApplication { public static void main(String[] args) { SpringApplication.run(SpringBootCloudApplication.class, args); } }
1.6、啟動項目
運行 Application.java 中的 main 方法,啟動 Eureka 服務,然後訪問 http://localhost:8761/:
到這裏位置,我們的Spring-cloud 服務治理 以及搭建好了,相較於搭建一個Zookeeper 服務,在下認為,還是這個比較方便,畢竟 Spring-cloud-Eureka 也是基於Java 實現,你覺得呢?
二、服務提供者
下面我們創建提供服務的客戶端,並向服務註冊中心註冊自己。本文我們主要介紹服務的註冊與發現,所以我們不妨在服務提供方中嘗試著提供一個接口來獲取當前所有的服務信息。
當client向server註冊時,它會提供一些元數據,例如主機和端口,URL,主頁等。Eureka server 從每個client實例接收心跳消息。 如果心跳超時,則通常將該實例從註冊server中刪除。
項目創建與上述構建方式類似,大家可以參照上述方法,再創建多一個Spring Cloud 項目。
2.1、Pom.xml
Maven 依賴 與上述類似:
<?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.jaycekon</groupId> <artifactId>spring-boot-cloud-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-boot-cloud-provider</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR3</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>View Code
2.2、Application.propertties
通過 spring.application.name 我們可以指定 服務提供者的名稱,在後續配合Ribbon 中,可以通過服務名稱來調用服務。
eureka.client.serviceUrl.defaultZone: 對應服務註冊中心的配置內容,指定服務註冊中心的位置
server.port:服務端口號
spring.application.name=eureka-client
server.port=2001
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
2.3、Application.java
在 Application.java 啟動類中添加 @EnableDiscoveryClient
註解,該註解能激活Eureka中的DiscoveryClient實現,這樣才能實現Controller中對服務信息的輸出。
大家可以看出,服務治理中,是使用的 @EnableEurekaServer 註解,這裏是使用的 @EnableDiscoveryClient ,從註解我們可以看出,一個適用於標識服務治理,一個是服務提供
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class SpringBootCloudProviderApplication { public static void main(String[] args) { new SpringApplicationBuilder( SpringBootCloudProviderApplication.class) .web(true).run(args); } }
2.4、啟動項目
在啟動 Productor 項目前,我們需要先啟動 Eureka 服務,不然 會出現TranspoertException:
com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
原因就是找不到服務註冊中心。
按順序啟動服務後,我們可以在Eureka 中發現,Productor 已經成註冊到了 Eureka 中
2.5、訪問服務
我們在 Productor 中配置一個接口,提供給客戶使用,具體服務如下:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Created by Jaycekon on 2017/9/20. */ @RestController public class DcController { @Autowired private DiscoveryClient discoveryClient; @RequestMapping("/product") public String product(String name) { return "Services: " + discoveryClient.getServices() + "name :" + name; } }
DiscoveryClient 用戶發現Eureka 中提供的服務,我們請求 /product 接口,查看返回結果:
通過 DiscoverClient 我們可以看到,Eureka 中提供的服務
三、服務消費者
在Spring Cloud Commons中提供了大量的與服務治理相關的抽象接口,例如上述的DiscoverClient。在這裏,我們將會學習一下 LoadBalancerClient 。
從 LoadBalancerClient 的命名 我們可以發現,它是一個負載均衡客戶端的抽象定義,接下來我們將會實現一個簡單的服務。
3.1、Pom.xml
<?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.jaycekon</groupId> <artifactId>spring-boot-cloud-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-boot-cloud-consumer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR3</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>View Code
3.2、Application.properties
spring.application.name=eureka-consumer
server.port=2101
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
3.3、Application.java
package com.jaycekon.cloud; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient public class SpringBootCloudConsumerApplication {public static void main(String[] args) { new SpringApplicationBuilder( SpringBootCloudConsumerApplication.class) .web(true).run(args); } }
3.4、服務接口
大家可以發現,上述的配置,跟之前的沒有太多的變化,因此我也沒有做解釋,相信大家都是能夠看得懂的,接下來,我們需要實現服務調用接口:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * Created by Jaycekon on 2017/9/20. */ @RestController public class DcController { @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/consumer") public String consumer() {
ServiceInstance serviceInstance = loadBalancerClient.choose("eureka-client"); String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/product?name=jaycekon"; System.out.println(url); return new RestTemplate().getForObject(url, String.class); } }
大家可以看到,我們這裏會通過 loadBalancerClient,來獲取服務的相關地址信息。
LoadBalancerClient 的choose 會通過負載均衡選出一個 “eureka-client” 服務實例,然後存儲在 ServicceInstance 中,然後通過接口的調用,訪問服務提供者的接口調用。
3.5、啟動服務
項目啟動順序:Eureka-> Productor -> Consumer
項目啟動完成後,我們訪問 http://localhost:2101/consumer:
可以看到,通過訪問Consumer ,我們已經成功調用了Productor 的服務了。
3.6、Ribbon
上述事例我們講解了使用 LoadBalancerClient 作為客戶端服務端負載均衡工具,在這裏我們可以了解一下使用 Ribbon 作為負載均衡。
首先在項目依賴上,我們需要引入Ribbon 的相關依賴:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency>
然後在Application 中,需要添加一個配置項:
@Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); }
在創建RestTemplate 實例時,需要引用 LoadBalanced 註解。
在接口調用中,實現Ribbon 調用:
@RestController public class DcController { @Autowired private LoadBalancerClient loadBalancerClient; @Autowired private RestTemplate restTemplate; @GetMapping("/consumer") public String consumer() { ServiceInstance serviceInstance = loadBalancerClient.choose("eureka-client"); String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/product?name=jaycekon"; return new RestTemplate().getForObject(url, String.class); } @GetMapping("/ribbon") public String ribbon() { return restTemplate.getForObject("http://eureka-client/product?name=jaycekon", String.class); } }
再啟動項目:
可以看到返回的結果是一樣的,這裏使用 Ribbon 的好處在於,Ribbon 會自動根據 生產者的服務名,在Eureka中找到對應的服務地址與端口。
當Ribbon與Eureka聯合使用時,ribbonServerList會被DiscoveryEnabledNIWSServerList重寫,擴展成從Eureka註冊中心中獲取服務實例列表。同時它也會用NIWSDiscoveryPing來取代IPing,它將職責委托給Eureka來確定服務端是否已經啟動。
總結:
Spring Cloud 的服務相對來說會比較完善,從一開始的圖中我們就可以看出,Spring cloud 提供了配置管理、服務發現、斷路器、智能路由、微代理、控制總線、全局鎖、決策競選、分布式會話和集群狀態管理等一系列的服務,其中包含了多個子項目:比如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud0 CloudFoundry、Spring Cloud AWS、Spring Cloud Security、Spring Cloud Commons、Spring Cloud Zookeeper、Spring Cloud CLI等項目。博主在後續的微服務選擇上,會繼續研究Spring Cloud 的知識。後續會持續更新Spring Cloud 的相關博文,希望大家支持。
博文中所用的代碼可以在我的 Github 中 找到:https://github.com/jaycekon
後續會持續更新,希望大家多多支持~~~~
Spring-Boot:Spring Cloud構建微服務架構