Spring Cloud Netflix — 服務消費
前言
題目本想寫服務消費者,可是又怕太強調消費者三個字,因為在Eureka中服務消費者同樣也可以作為服務生產者來提供服務,所以這裡只寫了服務消費。Eureka的服務消費有兩種方式,一種是rest+ribbon的方式,另一種是Feign的方式。
Rest+Ribbon
像上一篇部落格所講,建立一個Spring Boot專案,新增Ribbon的引用;如果是Spring Boot1.0,則新增Ribbon引用為:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency>
如果使用的Spring Boot2.0,Ribbon引用的id不同,新增Ribbon引用為:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
在application.properties檔案中新增配置:
server.port=8765 eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ spring.application.name=consumer-ribbon
在啟動類上添加註解@EnableEurekaClient,實現向Eureka Server的註冊,並用Bean的方式注入RestTemplate,如下所示:
@SpringBootApplication @EnableDiscoveryClient @RestController public class ConsumerRibbonApplication { public static void main(String[] args) { SpringApplication.run(ConsumerRibbonApplication.class, args); } @Bean @LoadBalanced RestTemplate restTemplate(){ return new RestTemplate(); } @Autowired private HelloService helloService; @GetMapping(value = "/ribbon/sayHello") public String hello(String name){ return helloService.hiService(name); } }
這裡沒有建立Controller層,直接將啟動類註解為Controller,具體的業務邏輯放在Service的實現中。Service所做的,就是根據要呼叫的服務地址呼叫生產者的服務:
@Service
public class HelloServiceImpl implements HelloService{
@Autowired
RestTemplate restTemplate;
@Override
public String hiService(String name) {
return restTemplate.getForObject("http://EUREKA-PRODUCER-HELLO/hello?name="+name,String.class);
}
}
如上述程式碼所示,Eureka在呼叫生產者服務時,只需要使用服務名稱即可,Ribbon會根據註冊該服務名的機器進行負載均衡。我所註冊的EUREKA-PRODUCER-HELLO名稱的機器有兩臺,用port來區分,在呼叫時印表機器的埠號。啟動consumer-ribbon時,訪問多次即可看到輸出的埠號的變化,即代表是多個服務生產者提供服務的。
Ribbon是客戶端負載,已經預設實現了以下所示的配置Bean:
Bean Type | Bean Name | Class Name |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Feign
利用這種方式呼叫,同樣按上一篇部落格介紹建立Spring Boot專案,然後新增Feign的依賴,同樣Spring Boot1.0和Spring Boot2.0的引用方式不同,下面是2.0的引用:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
同樣的在啟動類上添加註解@EnableEurekaClient以註冊到Eureka Server上,並新增@EnableFeignClients的註解,代表使用Feign的方式呼叫服務生產者,如下所示:
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@RestController
public class ConsumerFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerFeignApplication.class, args);
}
@Autowired
private HelloServiceFeignClient helloServiceFeignClient;
@GetMapping(value = "/feign/sayHello")
public String sayHello(@RequestParam("name") String name){
return helloServiceFeignClient.sayHello(name);
}
}
上面程式碼中的HelloServiceFeignClient是一個介面,用來呼叫其他服務,以類似於Restful的風格來呼叫:
@FeignClient(value = "EUREKA-PRODUCER-HELLO")
@Component
public interface HelloServiceFeignClient {
/**
* 打招呼
* @param name
* @return
*/
@GetMapping(value = "/hello")
String sayHello(@RequestParam("name") String name);
}
@FeignClient註解中的value代表要訪問的服務名稱,下面方法中的url代表訪問服務的url,@GetMapping的註解也可使用@RequestMapping(method = RequestMethod.GET)註解代替,post呼叫方式也一樣。對於引數來說,@RequestParam中的value要和服務方的引數名稱對應,如果傳入多個引數,可以使用@RequestBody註解實體來實現。
FeignClient預設繼承了Ribbon,所以這種呼叫方式的負載均衡也是Ribbon實現的。另外,如果一個服務中要呼叫多個服務時,課建立多個interface類,只要將@FeignClient的value改成相應的服務名即可,這樣注入不同的interface即可呼叫不同服務中的方法。
後語
個人認為FeignClient的方式還是比較好用,習慣了Restful的呼叫方式,感覺FeignClient的方式很順眼也很方便,而且合作開發時程式碼風格也比較統一。如果服務提供方提供了jar包就更好了,連interface都不用寫了,直接使用對方的jar包來呼叫Eureka服務,而且還可以使用對方的返回實體,自己動手的地方簡直太少了。只不過這樣的話,自己的服務還是要註冊到EurekaServer上才行,因為畢竟還是通過FeignClient的方式呼叫。