feign-使用最佳實踐

     Spring Cloud提供的微服務都是基於http,那麼在請求的時候如果用spring提供的RestTemplate再加一個Ribbon提供的@LoadBalanced註解也是可以比較方便的發起微服務的調用,並且也能做到負載均衡。但是如果服務提供者參數較多也較複雜,那麼使用這種方式需要拼接url或者使用Map的形式,但總歸是不便於開發的。
     Spring Cloud全家桶提供了feign這樣的一個組件,feign是Netflix開發的聲明式、模板化的HTTP客戶端,feign的使用非常簡單,創建一個接口,在接口上加入一些註解,這樣就完成了代碼開發。
     Spring Cloud在Netflix的基礎上對feign進行了進一步的增強,添加了對Spring MVC註解的支持,並整合了Ribbon和Eureka,從而讓feign的使用更加簡單了。接下去介紹使用feign的最佳實踐。
  • Api/Client
    我們在寫dubbo服務的時候,會定義一個接口Interface,然後提供者實現該接口完成具體的服務邏輯並將服務接口export出去,消費者注入該接口(需要有相應的配置)然後就能調用該服務。對於提供者的輸入和輸出同樣可以定義在一個同一個包裏面,然後提供者和消費者都來依賴,這樣可以減少代碼開發量。
    那麼同樣在feign的使用中,我們也來這麼做:
    • 依賴
compile ("org.springframework.cloud:spring-cloud-netflix-core:1.3.1.RELEASE") {
  exclude group: 'org.springframework.boot', module: 'spring-boot'
  exclude group: 'org.springframework.boot', module: 'spring-boot-autoconfigure'
}
compile ("org.springframework:spring-web:4.3.8.RELEASE")
               這裏使用的是Dalston.SR1版本,去掉spring-boot相關的包,因爲只需要@FeignClient這樣的註解,加入spring-web包是爲了要Spring MVC相關的註解
    • 接口
@FeignClient("eureka-client-test")
@RequestMapping("spring-cloud")
public interface HelloService {

  @RequestMapping(value = "hello1", method = RequestMethod.GET)
  String hello(@RequestParam(value = "name", required = false) String name);

  @RequestMapping(value = "hello2", method = RequestMethod.GET)
  MatchMessage hello(@RequestHeader(value = "name", required = false) String name,
  @RequestHeader(value = "age", required = false) Integer age);

  @RequestMapping(value = "hello3", method = RequestMethod.POST)
  MatchMessage hello(@RequestBody(required = false) UserDto userDto);
}
               @FeignClient表示的是哪個服務(註冊到Eureka server的服務名),@RequestMapping就是傳統我們熟悉的Spring MVC相關的註解
  • 輸入輸出
    MatchMessage和UserDto的定義比較簡單,不再貼代碼了,MatchMessage主要是服務返回的數據,UserDto是服務需要的參數
  • 服務提供者
    • 加入依賴
compile ("spring-cloud-demo:feign-client:0.0.1") {
  exclude group: 'org.springframework', module: 'spring-web'
  exclude group: 'org.springframework.cloud', module: 'spring-cloud-netflix-core'
}
               這裏服務的實現僅僅需要的是輸入和輸出的定義和實現那個Interface,所以無需其他相關的包,可以exclude掉
    • 服務實現
@RestController
public class HelloController implements HelloService {

  private static final Logger LOGGER = LoggerFactory.getLogger(HelloController.class);

  @Override
  public String hello(@RequestParam(value = "name", required = false) String name) {
  LOGGER.info("call hello, simple");
  return "Hello " + name;
  }

  @Override
  public MatchMessage hello(@RequestHeader(value = "name", required = false) String name,
  @RequestHeader(value = "age", required = false) Integer age) {
  UserDto userDto = new UserDto(name, age);
  LOGGER.info("call hello, GET");
  return new MatchMessage(userDto, MatchMessage.RESULT_MATCHED);
  }

  @Override
  public MatchMessage hello(@RequestBody(required = false) UserDto userDto) {
  LOGGER.info("call hello, POST");
  return new MatchMessage(userDto, MatchMessage.RESULT_MATCHED);
  }
}
               代碼比較簡單,實現HelloService接口,一些接口的定義如@RequestMapping就不再需要了,已經在client中定義了,這個其實就是feign對繼承的支持。
    • 調用
@RestController
public class HelloController {

  private static final Logger LOGGER = LoggerFactory.getLogger(HelloController.class);

  @Autowired
  private HelloService helloService;

  @RequestMapping(value = "feign-consumer", method = RequestMethod.GET)
  public MatchMessage consume1() {
  LOGGER.info(this.helloService.hello("levi.qian"));
  MatchMessage message = this.helloService.hello("levi.qian", 20);
  LOGGER.info(message.getResult() + "");
  return this.helloService.hello(new UserDto("levi.qian", 27));
  }
}
               注入HelloService,直接調方法即可,底層會使用Ribbon的負載均衡來調具體的服務。
     這樣做的方式將提供者和消費者的代碼耦合了,但是這種方式能夠減少很多的代碼,如果在前期對接口定義得足夠好,這種方式還是可以接受的。
     具體的Feign的原理會在以後講到(爲啥加了@Feign就可以調用了?)

     分享一個項目:https://github.com/JThink/SkyEye對java、scala等運行於jvm的程序進行實時日誌採集、索引和可視化,對系統進行進程級別的監控,對系統內部的操作進行策略性的報警、對分佈式的rpc調用進行trace跟蹤以便於進行性能分析。歡迎對分佈式跟蹤感興趣的交流~~,交流羣:624054633
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章