教程倉庫:github Spring-Cloud-Alibaba-Learning
教程索引頁:index Spring-Cloud-Alibaba-Learning
源碼地址
本篇內容的項目地址爲:微服務調用
目標
- 可以根據服務名獲取到該服務的節點列表
- 可以獲取到在服務註冊中心註冊成功的所有服務名
- 完成跨服務的遠程調用
準備工作
保證Nacos在啓動狀態,且確保兩個服務(service-a和service-b)每次啓動時都能成功註冊到Nacos上
流程
一、根據服務名獲取節點列表
我們在service-a中寫一個控制器,在裏面寫一個接口,獲取服務名爲service-b的節點列表。
-
我們需要使用SpringCloud爲我們提供的一個組件DiscoveryClient。
//由SpringCloud提供的組件,和Nacos解耦 //也就是說 如果我們不使用Nacos而是其他的服務發現組件,依然可以使用DiscoveryClient @Autowired private DiscoveryClient discoveryClient;
-
接下來寫一個接口來獲取服務名爲service-b的節點實例
/** * 測試服務發現 * 獲取服務名爲service-b的所有服務節點實例 * @return serviceInstanceList */ @GetMapping("testInstances") public List<ServiceInstance> getInstances(){ return discoveryClient.getInstances("service-b"); }
接下來做三次測試:
-
啓動service-a,不啓動service-b,測試接口testInstances,得到返回值如下:
[]
-
啓動service-a,啓動一個service-b實例,測試接口testInstances,得到返回值如下:
[ { "serviceId": "service-b", "host": "192.168.2.101", "port": 8182, "secure": false, "metadata": { "nacos.instanceId": "192.168.2.101#8182#DEFAULT#DEFAULT_GROUP@@service-b", "nacos.weight": "1.0", "nacos.cluster": "DEFAULT", "nacos.healthy": "true", "preserved.register.source": "SPRING_CLOUD" }, "uri": "http://192.168.2.101:8182" } ]
-
啓動service-a,啓動兩個service-b實例,測試接口testInstances,得到返回值如下:
[ { "serviceId": "service-b", "host": "192.168.2.101", "port": 8182, "secure": false, "metadata": { "nacos.instanceId": "192.168.2.101#8182#DEFAULT#DEFAULT_GROUP@@service-b", "nacos.weight": "1.0", "nacos.cluster": "DEFAULT", "nacos.healthy": "true", "preserved.register.source": "SPRING_CLOUD" }, "uri": "http://192.168.2.101:8182" }, { "serviceId": "service-b", "host": "192.168.2.101", "port": 8183, "secure": false, "metadata": { "nacos.instanceId": "192.168.2.101#8183#DEFAULT#DEFAULT_GROUP@@service-b", "nacos.weight": "1.0", "nacos.cluster": "DEFAULT", "nacos.healthy": "true", "preserved.register.source": "SPRING_CLOUD" }, "uri": "http://192.168.2.101:8183" } ]
至此,testInstances接口測試成功。我們可以看到確實是根據服務名獲取到了全部的節點列表。
二、根據服務名獲取節點列表
在這裏我們調用DiscoveryClient的另一個API:discoveryClient.getServices()。
/**
* 測試服務發現
* 獲取服務名列表
* @return serviceNamesList
*/
@GetMapping("testServices")
public List<String> getServiceNames(){
return discoveryClient.getServices();
}
然後啓動一下,會看到我們當前啓動的服務名列表:
[
"service-a",
"service-b"
]
三、完成跨服務的遠程調用
在這裏我們藉助RestTemplate來完成一個簡單的遠程調用。
-
在service-b中添加一個可供遠程調用的服務
@RestController public class TestController { @GetMapping("test/{argue}") public String test(@PathVariable("argue") String argue){ return "this is service-b, argue = " + argue; } }
-
在service-a中添加RestTemplate組件。在本例中直接加在了Application啓動類中,當然我們也可以單獨寫一個Configuration類
@Bean public RestTemplate restTemplate(){ return new RestTemplate(); }
-
編寫一個接口,裏面調用遠程服務,編碼思路如下:
- 藉助discoveryClient獲取到服務名爲service-b的所有運行中實例列表instances
- 取出instances中的第一個實例的uri,並追加*/test/{argue}字符串,得到targetUrl*,以定位到service-b中的一項具體服務
- 爲targetUrl填入參數,併發起get請求(restTemplate.getForObject)
- 返回遠程調用結果
@RestController @Slf4j public class RemotingController { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @GetMapping("remote") public String remote(){ List<ServiceInstance> instances = discoveryClient.getInstances("service-b"); //找到instances中第一個實例的地址(uri) String targetUrl = instances.stream() .map(instance -> instance.getUri().toString() + "/test/{argue}") .findFirst() .orElseThrow(() -> new IllegalArgumentException("當前沒有實例")); log.info("請求的目標地址:{}",targetUrl); //調用service-b的服務 //用http get請求,並且返回對象 return restTemplate.getForObject( targetUrl, String.class, "argue from service-a" ); } }
測試一下接口,結果如下:
this is service-b, argue = argue from service-a