本篇示範如何在一個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類別,也就是MemberApplication
及MessageApplication
。
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。
參考:
沒有留言:
張貼留言