Spring Cloud_3_微服務釋出與呼叫
阿新 • • 發佈:2019-02-08
微服務釋出與呼叫
- 認識Eureka框架
- 執行Eureka伺服器
- 釋出微服務
- 呼叫微服務
- 本章將講述SpringCloud中Eureka的使用,包括在Eureka伺服器上釋出、呼叫微服務,Eureka的配置以及叢集等內容
1、Eureka介紹
- 提供了Eureka伺服器端與客戶端
- 主要用於服務管理(維護服務列表,自動檢查其狀態)
- SpringCloud集成了Netflix OSS的多個專案,形成了Spring-Cloud-Netflix專案,該專案包含了多個子模組,這些子模組對整合的Netflix旗下框架進行了封裝
Spring Cloud Netflix提供了一系列搭建微服務基礎架構的功能元件
- Eureka(服務註冊與發現框架)
- Hystrix(服務容錯元件):容錯管理工具,旨在通過控制服務和第三方庫的節點,從而對延遲和故障提供強大的容錯能力
- Zuul(服務閘道器):邊緣服務工具,提供動態路由、監控等邊緣服務
- Ribbon(客戶端負載均衡器):提供客服端負載均衡演算法,將Netflix的中間層服務連線起來
- Feign(宣告式Http客戶端):可以建立宣告式、模板化的Http客戶端,進行微服務呼叫
本小節將講述其中一個較為重要的服務管理框架:Eureka[juˈri:kə]
1.1、關於Eureka
- 一個基於REST風格的服務元件,用於定位服務,以實現雲端的負載均衡和中間層伺服器的故障轉移
- 提供了基於Java的客戶端元件,使得與服務的互動更加方便
- 客戶端還有一個內建的負載均衡器,用於執行基本的輪詢負載均衡,為業務元件的叢集部署創造了條件
- 使用該框架,可以將業務元件註冊到Eureka容器中,進行叢集部署
- Eureka提供服務呼叫功能,可以釋出容器中的服務並進行呼叫
1.2、Eureka架構
- 一個簡單的Eureka叢集,需要一個Eureka伺服器、若干個服務提供者
- 將業務元件註冊到Eureka伺服器中,其他客戶端元件可以向伺服器獲取服務並且進行遠端呼叫
- 圖中有兩個伺服器,伺服器支援叢集部署,每個伺服器也可以作為對方伺服器的客戶端進行相互註冊與複製
- 3個Eureka客戶端,2個用於釋出服務,1個用於呼叫服務
- 不管是伺服器還是客戶端,都可以部署多個例項,因此,就很容易構建高可用的服務叢集
Eureka伺服器(114電話查詢平臺)
Eureka客戶端服務提供者(醫院、警察局電話放置114電話查詢平臺)
Eureka客戶端服務呼叫者(查詢電話的我們)
我們去114平臺查詢某處電話號碼(服務查詢),得到電話號碼後(獲取註冊資訊),撥打電話(服務呼叫)
- Eureka Server例項作為服務註冊中心的角色,負責接收來自Eureka Client的服務註冊,並加以儲存
- Eureka Client內嵌於一個微服務中,會將當前例項作為服務例項註冊中心(Eureka Server),註冊內容包括IP、Port和服務例項名稱
- 消費者同樣內嵌了Eureka Client,在進行微服務呼叫的時候,可以通過Eureka提供的DiscoveryClient和服務例項的名稱,從服務註冊中心(Eureka Server)獲取服務例項列表,然後得到服務例項的IP和Port,以完成服務呼叫
1.3、伺服器端
對於註冊到伺服器的服務元件,Eureka伺服器並沒有提供後臺的儲存,這些註冊的服務例項被儲存在記憶體的服務註冊中心,它們通過心跳來保持其最新狀態,這些操作都可以在記憶體中完成。
客戶端存在相同的機制,同樣在記憶體中儲存了登錄檔資訊,這樣的機制提升了Eureka元件效能,每次服務的請求都不用經過伺服器端的註冊中心。
1.4、服務提供者
作為Eureka客戶端存在的服務提供者,主要進行以下工作:
第一:向伺服器註冊服務
第二:傳送心跳給伺服器
第三:向伺服器端獲取註冊列表
當客戶端註冊到伺服器時,它將會提供一些關於它自己的資訊給伺服器端,例如:IP、Port、健康連結等
1.5、服務呼叫者
對於釋出到Eureka伺服器的服務,使用呼叫者可對其進行服務查詢和呼叫,服務呼叫者也是作為客戶端存在,但是其職責主要是發現和呼叫服務。
在實際情況中,有可能出現本身既是服務提供者,又是服務呼叫者的情況,例如:傳統的企業應用三層架構中,服務層(service)會呼叫資料訪問層(dao)的介面進行資料操作,它本身也會提供服務給控制層(controller)使用。
2、Eureka應用
2.1、構建伺服器
- 建立一個maven專案:atm-eureka-server
2.1.1、新增依賴
- 在pom.xml中新增Spirng Cloud的依賴
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atm.cloud</groupId>
<artifactId>atm_enreka_server</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>atm_enreka_server Maven Webapp</name>
<url>http://maven.apache.org</url>
<!-- Spring Cloud -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!--不需要再引入-->
<!--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
-->
<!--Eureka-->
<!--
加入的spring-cloud-starter-eureka-server會自動引入spring-boot-starter-web
因此只需要加入該依賴,我們的專案就具有 Web 容器的功能
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<finalName>atm_enreka_server</finalName>
</build>
</project>
2.1.2、編寫啟動類
package com.atm.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class MyApplicationServer {
public static void main(String[] args) {
SpringApplication.run(MyApplicationServer.class, args);
}
}
- 可以發現與SpringBoot的啟動類基本沒有差異,只是加入了@EnableEurekaServer,宣告這是一個 Eureka 伺服器。直接執行 FirstServer 即可啟動 Eureka 伺服器
- application.yml(Eureka預設 localhost:8761)
server:
port: 8761
- 執行main方法,啟動,雖然啟動成功,但是出現了異常
- 異常暫時不管,先進行訪問
- 可以看到服務的例項列表,目前我們並沒有註冊服務,因此列表為空
2.1.3、伺服器註冊開關(解決異常)
- 在上面我們已經發現,啟動的時候會出現異常
- 這是由於在伺服器啟動時,伺服器會把自己當作一個客戶端,去 Eureka 伺服器註冊,並且會到 Eureka 伺服器抓取註冊資訊,它自己本身只是一個伺服器,而不是服務的提供者(客戶端),因此可以修改application.yml 檔案,修改以下兩個配置:
server:
port: 8761
eureka:
client:
registerWithEureka: false
fetchRegistry: false
2.2、編寫服務提供者
- 新建立一個maven專案:atm_eureka_provider
2.2.1、新增依賴
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atm.cloud</groupId>
<artifactId>atm_enreka_provider</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>atm_enreka_provider Maven Webapp</name>
<url>http://maven.apache.org</url>
<!-- Spring Cloud -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- 服務提供者 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- 服務提供者 -->
</dependencies>
<build>
<finalName>atm_enreka_provider</finalName>
</build>
</project>
2.2.2、配置檔案
server:
port: 8080
spring:
application:
name: first-service-provider
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
hostname: localhost
2.2.3、編寫控制器
- Person類
package com.atm.cloud;
public class Person {
private Integer id;
private String name;
private Integer age;
//...省略setter/getter...
}
- MyController類
package com.atm.cloud;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@RequestMapping(value = "/info", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Person findPerson(){
Person person=new Person();
person.setId(1);
person.setAge(18);
person.setName("atm");
return person;
}
@RequestMapping(value = "/person/{personId}", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Person findPerson(@PathVariable("personId")Integer personId){
Person person=new Person();
person.setId(personId);
person.setAge(18);
person.setName("atm");
return person;
}
}
2.2.4、編寫啟動類
- @EnableEurekaClient,預設註冊到8761的伺服器上
package com.atm.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class MyApplicationProvider {
public static void main(String[] args) {
SpringApplication.run(MyApplicationProvider.class, args);
}
}
- 啟動伺服器MyApplicationServer、啟動服務提供者MyApplicationProvider
2.3、編寫服務呼叫者
- 服務被註冊、釋出到Euraka伺服器後,就需要程式去發現它,並且進行呼叫
- 此處所說的呼叫者,是指同樣註冊到Eureka的客戶端,來呼叫其他客戶端釋出的服務,簡單來說,就是Eureka內部呼叫
- 由於同一個服務,可能會部署多個例項,呼叫過程可能涉及負載均衡,伺服器查詢等問題,Netflix已經幫我們解決了這個問題,並且SpringCloud已經封裝了一次
- 建立新maven專案:atm_eureka_invoker
2.3.1、新增依賴
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atm.cloud</groupId>
<artifactId>atm_enreka_invoker</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>atm_enreka_invoker Maven Webapp</name>
<url>http://maven.apache.org</url>
<!-- Spring Cloud -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- 服務呼叫者 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency><!-- 負載均衡框架 -->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<!-- 服務呼叫者 -->
</dependencies>
<build>
<finalName>atm_enreka_invoker</finalName>
</build>
</project>
2.3.2、配置檔案
server:
port: 9000
spring:
application:
name: first-service-invoker
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
- 在配置檔案中,配置了應用名稱為 “first-service-invoker”,這個呼叫者的訪問埠為9000,需要注意的是,這個呼叫本身也可以對外提供服務
- 與提供者一樣,使用eureka的配置,將呼叫者註冊到“atm_enreka_server”
2.3.3、編寫控制器
package com.atm.cloud;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Configuration
public class InvokerController {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@RequestMapping(value="/router",method=RequestMethod.GET,
produces=MediaType.APPLICATION_JSON_VALUE)
public String router() {
RestTemplate restTemplate = getRestTemplate();
// 根據應用名稱呼叫服務
String json = restTemplate.getForObject(
"http://first-service-provider/person/1", String.class);
return json;
}
}
- 在控制器中,配置了RestTemplate的bean,RestTemplate本來是spring-web模組下的類,主要用呼叫REST服務,本身並不具備呼叫分散式服務的能力,但是RestTemplate的bean被@LoadBalanced註解修飾後,這個RestTemplate例項就具備訪問分散式服務的能力
2.3.4、編寫啟動類
- 在控制器中,新建了一個router的測試方法,用來對外發布REST服務,該方法只是一個路由的作用
- 實際上,使用RestTemplate來呼叫“first-service-provider”(服務者提供)的服務
- 需要注意的是,僅僅是通過服務名稱來進行呼叫
package com.atm.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class MyApplicationInvoker {
public static void main(String[] args) {
SpringApplication.run(MyApplicationInvoker.class, args);
}
}
- 啟動類中,使用@EnableDiscoveryClient註解來修飾啟動類
- 該註解使得服務呼叫者,有能力去Eureka中發現服務
- 需要注意的是@EnableEurekaClient中已經包含@EnableDiscoveryClient的功能
- 也就是說,一個Eureka客戶端,本身就具有發現服務的能力
- 啟動伺服器(atm_eureka_server)
- 啟動服務提供者(atm_eureka_provider)
- 啟動服務呼叫者(atm_eureka_invoker)
- 根據輸出可知,實際上呼叫了服務提供者的person/1
2.4、程式結構
- 本案例新建了三個專案
- Eureka 服 務 為 本 例 的 “ atm_eureka_server ”,服務釋出者為“atm_eureka_provider”,服務呼叫者為“atm_eureka_invoker”
- 使用者通過瀏覽器訪問呼叫者的9000埠的router服務,router服務中查詢伺服器提供者的服務並進行呼叫