在前面的文章中可以發現當我們通過RestTemplate調用其它服務的API時,所需要的參數須在請求的URL中進行拼接,如果參數少的話或許我們還可以忍受,一旦有多個參數的話,這時拼接請求字元串就會效率低下,并且顯得好傻。
那麼有沒有更好的解決方案呢?答案是确定的有,Netflix已經為我們提供了一個架構:Feign。
Feign是一個聲明式的Web Service用戶端,它的目的就是讓Web Service調用更加簡單。Feign提供了HTTP請求的模闆,通過編寫簡單的接口和插入注解,就可以定義好HTTP請求的參數、格式、位址等資訊。
而Feign則會完全代理HTTP請求,我們隻需要像調用方法一樣調用它就可以完成服務請求及相關處理。Feign整合了Ribbon和Hystrix(關于Hystrix我們後面再講),可以讓我們不再需要顯式地使用這兩個元件。
總起來說,Feign具有如下特性:
- 可插拔的注解支援,包括Feign注解和JAX-RS注解;
- 支援可插拔的HTTP編碼器和解碼器;
- 支援Hystrix和它的Fallback;
- 支援Ribbon的負載均衡;
- 支援HTTP請求和響應的壓縮。
這看起來有點像我們springmvc模式的Controller層的RequestMapping映射。這種模式是我們非常喜歡的。Feign是用@FeignClient來映射服務的。
首先第一步,在原來的基礎上建立一個Feign子產品,接着引入相關依賴,引入Feign依賴,會自動引入Hystrix依賴的,如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
application.yml配置如下:
server:
port: 8083
spring:
application:
name: feign-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8888/eureka/,http://localhost:8889/eureka/
接着在前面文章中的的的兩個provider1和provider2兩個子產品的服務新增幾個方法,如下代碼所示:
/**
* Created by cong on 2018/5/8.
*/
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
System.out.println("通路來1了......");
return "hello1";
}
@RequestMapping("/hjcs")
public List<String> laowangs(String ids){
List<String> list = new ArrayList<>();
list.add("laowang1");
list.add("laowang2");
list.add("laowang3");
return list;
}
//新增的方法
@RequestMapping(value = "/hellol", method= RequestMethod.GET)
public String hello(@RequestParam String name) {
return "Hello " + name;
}
@RequestMapping(value = "/hello2", method= RequestMethod.GET)
public User hello(@RequestHeader String name, @RequestHeader Integer age) {
return new User(name, age);
}
@RequestMapping(value = "/hello3", method = RequestMethod.POST)
public String hello (@RequestBody User user) {
return "Hello "+ user. getName () + ", " + user. getAge ();
}
}
接着是上面代碼所需用到的User類,代碼如下:
/**
* Created by cong 2017/12/2.
*/
public class User {
private String name;
private Integer age;
//序列化傳輸的時候必須要有空構造方法,不然會出錯
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
接下來用Feign的@FeignClient(“服務名稱”)映射服務調用。代碼如下:
package hjc;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.*;
/**
* Created by cong on 2018/5/17.
*/
//configuration = xxx.class 這個類配置Hystrix的一些精确屬性
//value=“你用到的服務名稱”
@FeignClient(value = "hello-service",fallback = FeignFallBack.class)
public interface FeignService {
//服務中方法的映射路徑
@RequestMapping("/hello")
String hello();
@RequestMapping(value = "/hellol", method= RequestMethod.GET)
String hello(@RequestParam("name") String name) ;
@RequestMapping(value = "/hello2", method= RequestMethod.GET)
User hello(@RequestHeader("name") String name, @RequestHeader("age") Integer age);
@RequestMapping(value = "/hello3", method= RequestMethod.POST)
String hello(@RequestBody User user);
}
接着在Controller層注入FeiService這個接口,進行遠端服務調用,代碼如下:
/**
* Created by cong on 2018/5/17.
*/
@RestController
public class ConsumerController {
@Autowired
FeignService feignService;
@RequestMapping("/consumer")
public String helloConsumer(){
return feignService.hello();
}
@RequestMapping("/consumer2")
public String helloConsumer2(){
String r1 = feignService.hello("hjc");
String r2 = feignService.hello("hjc", 23).toString();
String r3 = feignService.hello(new User("hjc", 23));
return r1 + "-----" + r2 + "----" + r3;
}
}
接着在Feign子產品的啟動類哪裡打上Eureka用戶端的注解@EnableDiscoveryClient Feign用戶端的注解@EnableFeignClients,代碼如下:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
接着啟動啟動類,浏覽器上輸入localhost:8083/consumer 運作結果如下:

可以看到負載均衡輪詢出現hello1,hello2。
接着繼續在浏覽器上輸入localhost:8083/consumer2,運作結果如下:
接下來我們進行Feign聲明式調用服務下的,服務降級的使用,那麼我們就必須建立一個FeignFallBack類來繼承FeiService,代碼如下:
package hjc;
import org.springframework.stereotype.Component;
/**
* Created by cong on 2018/5/17.
*/
@Component
public class FeignFallBack implements FeignService{
//實作的方法是服務調用的降級方法
@Override
public String hello() {
return "error";
}
@Override
public String hello(String name) {
return "error";
}
@Override
public User hello(String name, Integer age) {
return new User();
}
@Override
public String hello(User user) {
return "error";
}
}
接着我們再把那兩個服務提供子產品provider1,provider2子產品進行停止,運作結果如下所示:
可以看到我們這幾個調用,都進行了服務降級了。
那麼如果我們想精确的控制一下Hystrix的參數也是可以的,比方說跟Hystrix結合的參數,那麼可以在FeignClient注解裡面配置一個Configuration=XXX類.class屬性,在哪個類裡面精确的指定一下屬性。
或者在application.yml裡面配置,如下:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutinMilliseconds: 5000
ribbon:
connectTimeout: 500
#如果想對單獨的某個服務進行詳細配置,如下
hello-service:
ribbon:
connectTimeout: 500
這裡滿足了我們大部分場景的調用,但是有寫精細場景,還是要用原生的Hystrix,跟我們之前的Hystrix用法一下,不要走Feign用戶端調用就行了,如下:
/**
* Created by cong on 2018/5/17.
*/
public class HjcCommand extends HystrixCommand {
protected HjcCommand(HystrixCommandGroupKey group) {
super(group);
}
@Override
protected Object run() throws Exception {
return null;
}
}
那麼我們如果想用原聲的HystrixCommand去搞一個異步請求怎麼辦?代碼如下:
首先再引入一個依賴,feign用戶端沒有預設引入進來,需要我們自己引入:
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>1.5.9</version>
</dependency>
接着用HystrixCommand注解方式實作:
/**
* Created by cong on 2018/5/17.
*/
@Service
public class HjcCommand {
@Autowired
private FeignService feignService;
//同步方式
@HystrixCommand
public Future<String> getEmployeesAsync(){
return new AsyncResult<String>() {
@Override
public String invoke() {
return feignService.hello("hjc");
}
};
}
//用同步方式還不如直接用Feign用戶端
@HystrixCommand
public String getEmployeesAsync1(){
return feignService.hello("laowang");
}
}
這樣還不行,我們還需要聲明一個切面,HystrixConfiguration,接着,将HystrixConfiguration加入到spring管理,代碼如下:
@Configuration
public class HystrixConfiguration {
@Bean
public HystrixCommandAspect hystrixAspect(){
return new HystrixCommandAspect();
}
}