1. 程式人生 > >Spring Cloud Netflix — 服務消費

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 TypeBean NameClass Name

IClientConfig

ribbonClientConfig

DefaultClientConfigImpl

IRule

ribbonRule

ZoneAvoidanceRule

IPing

ribbonPing

DummyPing

ServerList<Server>

ribbonServerList

ConfigurationBasedServerList

ServerListFilter<Server>

ribbonServerListFilter

ZonePreferenceServerListFilter

ILoadBalancer

ribbonLoadBalancer

ZoneAwareLoadBalancer

ServerListUpdater

ribbonServerListUpdater

PollingServerListUpdater

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的方式呼叫。