網頁

2019/6/24

Spring Cloud Eureka 使用RestTemplate實作服務間溝通(Service to Service Communication)範例一

本篇示範如何在一個Spring Cloud Eureka的一個服務透過RestTemplate呼叫另一外一個服務的REST API。

請先參考Spring Cloud Netflix Eureka簡介Spring Cloud 建立 Spring Cloud Netflix Eureka Server並建立一個Eureka Server。

接著在Eclipse建立兩個Eureka Client服務專案,分別為Member Service與Message Service。


Member Service




Message Service




兩個專案的Project Dependencies都加入Eureka Discovery Client和Spring (Boot) Web Starter。



Member Service和Message Service的application.yml如下。
兩個服務指向的Eureka Server位址為建立好的Eureka Server的位置,然後注意spring.application.name的值為服務名稱。

Member service - application.yml

# Spring properties
spring:
  application:
     name: member-service
# Discovery Server Access
eureka:
  client:
    service-url:
      default-zone: http://localhost:8761/eureka/
# HTTP Server
server:
  port: 2223   # HTTP (Tomcat) port

Message service - application.yml

# Spring properties
spring:
  application:
     name: message-service
# Discovery Server Access
eureka:
  client:
    service-url:
      default-zone: http://localhost:8761/eureka/
# HTTP Server
server:
  port: 2224   # HTTP (Tomcat) port

修改兩個服務的SpringBootApplication類別,也就是MemberApplicationMessageApplication

Member service - MemberApplication

package com.abc.member;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.client.RestTemplate;

import com.netflix.discovery.EurekaClient;

@EnableDiscoveryClient // <-- 加上這個
@SpringBootApplication
public class MemberApplication {

    public static void main(String[] args) {
        
        ConfigurableApplicationContext ctx = SpringApplication.run(MemberApplication.class, args);
        EurekaClient client = ctx.getBean(EurekaClient.class); // 取得EurekaClient的bean
        String url = client.getNextServerFromEureka("message-service", false).getHomePageUrl(); // 取得Message服務的URL位址
        
        System.out.println("send request start...");
        sendRequest(url);
        System.out.println("send request end...");
    }
    
    private static void sendRequest(String url) {
        final RestTemplate restTemplate = new RestTemplateBuilder().build();

        int memberId = 1;
        url = new StringBuilder(url).append("/messages/").append(memberId).toString();
        
        final String response = restTemplate.getForObject(url, String.class); // 呼叫Message服務的REST API,http://localhost:2224/messages/{messageId}
        System.out.println(response);
    }
    
}

在Member服務啟動時,取得EurekaClient的實例,呼叫getNextServerFromEureka(String virtualHostname, boolean secure)並傳入Message服務的名稱message-service來取得該服務的url位址,然後利用RestTemplate去調用Message服務的REST API,並將回傳的結果印出。

由於上面程式只會在Member服務啟動時執行一次,所以必須等Message服務啟動並註冊於Eureka Server後再啟動Member服務才能正確地呼叫到Message服務的REST API,否則會出現下面錯誤。

Exception in thread "main" java.lang.RuntimeException: No matches for the virtual host name :message-service

Message service - MessageApplication

package com.abc.message;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient // <-- 加上這個
@SpringBootApplication
public class MessageApplication {

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

}

在Message服務新增一個MessageController,用來接收Member服務送來的請求並返回結果。

Message service - MessageController

package com.abc.message.controller;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@RestController
@RequestMapping("messages")
public class MessageController {
    
    @GetMapping(value = "/{memberId}", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String getAllMessagesByMemberId(@PathVariable int memberId) throws JsonProcessingException {
        List<String> messages = findMessagesByMemberId(memberId);
        String jsonString = new ObjectMapper().writeValueAsString(messages);
        return jsonString; 
    }
    
    private List<String> findMessagesByMemberId(int memberId) {
        Map<Integer, List<String>> allMessages = new HashMap<>();
        allMessages.put(1, new ArrayList<String>(Arrays.asList("謝謝大大無私地分享", "樓主一生平安")));
        allMessages.put(2, new ArrayList<String>(Arrays.asList("樓主好人,純推不下", "祝大大平安喜樂")));
        
        return allMessages.get(memberId);
    }
    
}

全部專案如下。



Member服務的專案結構。



Message服務的專案結構。



完成以上專案後,依順序啟動Eureka Server專案 → Message服務專案 → Member服務專案。

原因是Eureka Server必須先啟動,其他Eureka Client的服務才能向其註冊。而Member服務啟動時會調用Message服務的REST API,所以Message服務要先向Eureka Server註冊,Member服務在啟動時透過服務名稱來查找Message服務的位址時才不會出現前述找不到的錯誤。


最後在Member服務專案的console會印出以下結果。

send request start...
["謝謝大大無私地分享","樓主一生平安"]
send request end...

以上服務啟動後在Eureka Server的UI畫面如下(http://localhost:8761/)。



可以看到Eureka Server註冊了兩個服務實例:

  • MEMBER-SERVICE
  • MESSAGE-SERVICE

或是參考Spring Cloud Eureka 使用RestTemplate實作服務間溝通範例二在Member服務的MemberController中使用RestTemplate來呼叫Message服務的API。


參考:

沒有留言:

張貼留言