SpringCloud之服務註冊和消費

系統架構

在沒有微服務之前有已經有跨服務調用了,比如ServiceB去調用ServiceA中的服務 , 傳統模式可以直接在ServiceB中寫ServiceA中的服務但是這樣是寫死了的,不夠靈活。
下圖就是傳統的調用
在這裏插入圖片描述
微服務下的跨系統調用應該是這樣的:

在這裏插入圖片描述
此時服務的調用應該是分兩個步驟的:
ServiceB 去服務中心拿到ServiceA的地址,如果ServiceA是單機部署,那麼這個地址就只有一個,如果ServiceA是集羣是集羣環境部署,那麼發現的地址就是多個。

拿到了ServiceA的地址後,ServiceB再去調用ServiceA的相關服務了。

這樣做其實是有很多好處的,首先互相調用的地址可以不用寫死,需要的時候直接去服務中心獲取,並且服務之間也可以很方便的部署、集羣等。

服務註冊與消費搭建

  1. 首先我們創建一個ServiceRegister的普通maven項目,然後在創建一個Eureka的SpringBoot項目作爲子項目
    在這裏插入圖片描述
    在這裏插入圖片描述

下面是Eureka項目的pom.xml 配置

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>

下面是application.yml 配置文件

# 別名
spring:
  application:
    name: eureka
#端口
server:
  port: 1111

# eureka config
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

注意 這裏配置好了後需要在Eureka這個子項目的啓動類上加入下面這個註解

@EnableEurekaServer

這個註解的意義是代表這個項目成爲一個註冊中心

  1. 創建一個Provider
    創建一個叫Provider的SpringBoot項目作爲子項目,pom.xml配置
<properties>
    <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </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>

下面是application.yml 配置文件

# 別名
spring:
  application:
    name: provider
#端口
server:
  port: 4001
# provider config
eureka:
  client:
    service-url:
      defaultZone: http://localhost:1111/eureka  

spring.application.name 是服務的名稱,你可以理解成是別名,其他的服務調用的時候需要使用這個name來調用

server.port 是端口號

eureka.client.service-url.defaultZone 是這個服務需要註冊到服務中心地址,這裏需要注意的是,如果服務中心是一個集羣,這裏也可以只寫服務中心的一個節點,eureka會自動集羣對服務進行同步。

在微服務中,你的項目的pom.xml 中如果存在 spring-cloud-starter-netflix-eureka-client的依賴,並且提供了eureka註冊中心的地址那麼會默認註冊到 Eureka Server 中。

然後我們在 provider 中提供一個簡單的服務

@RestController
public class SayHelloController {
  @GetMapping("/sayHello")
  public String SayHello(String name) {
    return "sayHello" + name + "!";
  }
}

這樣我們就創建好我們的服務提供者,並且提供了一個簡單的服務接口了。

  1. 創建consumer
    創建好了服務提供者,那麼我們就來創建服務消費者consumer,創建一個SpringBoot的子項目工程 pom依賴如下

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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>

這個配置和provider 配置幾乎是一樣的,下面是consumer的application.yml 配置文件

# 別名
spring:
  application:
    name: consumer
#端口
server:
  port: 4002
# provider config
eureka:
  client:
    service-url:
      defaultZone: http://localhost:1111/eureka  

唯一變了就是服務的名稱。

配置好了後,我們在consumer的啓動類中添加一個RestTemplate的實例,RestTemplate是Spring提供的一個Http請求工具,下面是這個RestTemplate實例的代碼

@SpringBootApplication
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

然後我們在consumer 中添加一個UserSayHelloController,在這裏去調用provider 提供的服務。

@RestController
public class SayUserHelloController {
    @Autowired
    DiscoveryClient discoveryClient;
    @Autowired
    RestTemplate restTemplate;
    @GetMapping("/sayHello")
    public String hello(String name) {
        List<ServiceInstance> list = discoveryClient.getInstances("provider");
        ServiceInstance instance = list.get(0);
        String host = instance.getHost();
        int port = instance.getPort();
        String s = restTemplate.getForObject("http://" + host + ":" + port + "/hello?name={1}", String.class, name);
        return s;
    }
}

上面代碼的意思是解釋如下:
首先我們在這個Controller中注入了一個DiscoveryClient ,DiscoveryClient可以從Eureka或者從Consul上根據服務名查詢一個服務的詳細信息,注意DiscoveryClient是下面這個包中的

org.springframework.cloud.client.discovery.DiscoveryClient
 @Autowired
 RestTemplate restTemplate;

RestTemplate 就是Spring 給我們提供用來發送Http請求的,這個大多數人應該都是知道的。

List<ServiceInstance> list = discoveryClient.getInstances("provider");

discoveryClient.getInstances 就是調用服務的名稱,爲什麼用List去接受? 那是因爲有可能provider 是單機部署 也有可能是集羣部署,如果是集羣部署的話,那麼provider的實例就有多個

ServiceInstance 保存了provider 中 詳細的信息、如主機地址、端口號、實例id等。ServiceInstance是一個接口,它有很多給實現類,我們本次的這個項目使用的是EurekaServiceInstance。

ServiceInstance instance = list.get(0);

因爲我們只有一個provider 實例,所以我們就用list.get(0) 來獲取實例了

獲取主機地址

String host = instance.getHost();

獲取端口號
int port = instance.getPort();

RestTemplate 的 getForObject 方法接收三個參數。第一個參數是請求地址,請求地址中的 {1} 表示一個參數佔位符,第一個參數 String.class 表示返回的參數類型,第三個參數則是一個第一個佔位符的具體值。

String s = restTemplate.getForObject("http://" + host + ":" + port + "/hello?name={1}", String.class, name);

最後我們返回這個s 就完成了consumer的編寫

下面我們依次的啓動Eureka(註冊中心)和 服務提供者(provider) 以及 消費者(consumer)

然後在瀏覽器上輸入:
localhost:4002/sayHello?name=jishu
結果如下圖所示:
在這裏插入圖片描述

這樣我們就順利的從consumer 去調用了provider 的服務了

DiscoveryClient是哪裏來的

歸根結底 DiscoveryClient作用就是可以從Eureka 或者Consul 中查詢服務的實例,不過DiscoveryClient就是一個接口而已,但是還是有一個實現類, 這個具體的實現類就是CompositeDiscovery,當我們的微服務啓動的時候,就會在CompositeDiscoveryClientAutoConfiguration類中配置一個CompositeDiscovery的實例,
下面這個就是大名鼎鼎的CompositeDiscoveryClientAutoConfiguration的源碼

//這裏就是DiscoveryClient的源碼了
@Configuration
@AutoConfigureBefore(SimpleDiscoveryClientAutoConfiguration.class)
public class CompositeDiscoveryClientAutoConfiguration {

  //這裏返回的是一個實例
	@Bean
	@Primary
	public CompositeDiscoveryClient compositeDiscoveryClient(
			List<DiscoveryClient> discoveryClients) {
		return new CompositeDiscoveryClient(discoveryClients);
	}

}

其實真正調用的是CompositeDiscoveryClient類中的discoveryClients 屬性提供的 DiscoveryClient,而discoveryClients 屬性默認集合中只有一條數據,就是EurekaDiscoveryClient,最終在EurekaDiscoveryClient類中,通過EurekaClient來獲取一個微服務的實例信息了

總結

在不用微服務調用,服務之間的調用是相當繁瑣的,並且地址是寫死了,那麼部署的話是非常的不方便,但是我們提供了註冊中心的話,那麼我們讓服務註冊到我們的註冊中心中,然後在從註冊中心去獲取我們的服務信息,這樣就有大大的好處,降低了調用的難度。

項目地址

github

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章