1. 程式人生 > >Spring Cloud_3_微服務釋出與呼叫

Spring Cloud_3_微服務釋出與呼叫

微服務釋出與呼叫

  • 認識Eureka框架
  • 執行Eureka伺服器
  • 釋出微服務
  • 呼叫微服務
  • 本章將講述SpringCloud中Eureka的使用,包括在Eureka伺服器上釋出、呼叫微服務,Eureka的配置以及叢集等內容

1、Eureka介紹

  • 提供了Eureka伺服器端與客戶端
  • 主要用於服務管理(維護服務列表,自動檢查其狀態)
  • SpringCloud集成了Netflix OSS的多個專案,形成了Spring-Cloud-Netflix專案,該專案包含了多個子模組,這些子模組對整合的Netflix旗下框架進行了封裝
  • Spring Cloud Netflix提供了一系列搭建微服務基礎架構的功能元件

    1. Eureka(服務註冊與發現框架)
    2. Hystrix(服務容錯元件):容錯管理工具,旨在通過控制服務和第三方庫的節點,從而對延遲和故障提供強大的容錯能力
    3. Zuul(服務閘道器):邊緣服務工具,提供動態路由、監控等邊緣服務
    4. Ribbon(客戶端負載均衡器):提供客服端負載均衡演算法,將Netflix的中間層服務連線起來
    5. 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服務中查詢伺服器提供者的服務並進行呼叫