使用Feign實現遠程HTTP調用
什么是Feign
- Feign是Netflix開源的聲明式HTTP客戶端
- GitHub地址:https://github.com/openfeign/feign
實現
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
@MapperScan("com.itmuch.contentcenter.dao")
@SpringBootApplication
@EnableFeignClients// (defaultConfiguration = GlobalFeignConfiguration.class)
@EnableBinding({Source.class})
public class ContentCenterApplication {
@FeignClient(name = "user-center")
public interface UserCenterFeignClient {
/**
* http://user-center/users/{id}
*
* @param id
* @return
*/
@GetMapping("/users/{id}")
UserDTO findById(@PathVariable Integer id);
}
private final UserCenterFeignClient userCenterFeignClient;
// 1. 代碼不可讀
// 2. 復雜的url難以維護:https://user-center/s?ie={ie}&f={f}&rsv_bp=1&rsv_idx=1&tn=baidu&wd=a&rsv_pq=c86459bd002cfbaa&rsv_t=edb19hb%2BvO%2BTySu8dtmbl%2F9dCK%2FIgdyUX%2BxuFYuE0G08aHH5FkeP3n3BXxw&rqlang=cn&rsv_enter=1&rsv_sug3=1&rsv_sug2=0&inputT=611&rsv_sug4=611
// 3. 難以相應需求的變化,變化很沒有幸福感
// 4. 編程體驗不統一
UserDTO userDTO = this.userCenterFeignClient.findById(userId);
Feign的組成
細粒度配置自定義
- Java代碼方式
- 配置屬性方法
指定日志級別
Java代碼方式
UserCenterFeignClient
@FeignClient(name = "user-center", configuration = GlobalFeignConfiguration.class)
public interface UserCenterFeignClient {
/**
* http://user-center/users/{id}
*
* @param id
* @return
*/
@GetMapping("/users/{id}")
UserDTO findById(@PathVariable Integer id);
}
GlobalFeignConfiguration
/**
* feign的配置類
* 這個類別加@Configuration注解了,否則必須挪到@ComponentScan能掃描的包以外
*/
public class GlobalFeignConfiguration {
@Bean
public Logger.Level level(){
// 讓feign打印所有請求的細節
return Logger.Level.FULL;
}
}
application.yml
logging:
level:
com.itmuch.contentcenter.feignclient.UserCenterFeignClient: debug
配置屬性方法
全局配置
- Java代碼方式
- 配置屬性方式
Java代碼方式
ContentCenterApplication
EnableFeignClients
// 掃描mybatis哪些包里面的接口
@MapperScan("com.itmuch.contentcenter.dao")
@SpringBootApplication
@EnableFeignClients(defaultConfiguration = GlobalFeignConfiguration.class)
@EnableBinding({Source.class})
public class ContentCenterApplication {
/**
* feign的配置類
* 這個類別加@Configuration注解了,否則必須挪到@ComponentScan能掃描的包以外
*/
public class GlobalFeignConfiguration {
@Bean
public Logger.Level level(){
// 讓feign打印所有請求的細節
return Logger.Level.FULL;
}
}
配置屬性方式
// 掃描mybatis哪些包里面的接口
@MapperScan("com.itmuch.contentcenter.dao")
@SpringBootApplication
@EnableFeignClients// (defaultConfiguration = GlobalFeignConfiguration.class)
@EnableBinding({Source.class})
public class ContentCenterApplication {
支持的配置項
Java代碼方式支持的配置項
配置屬性方式支持的配置項
配置最佳實踐
Ribbon配置 VS Feign配置
Feign代碼方式 vs 屬性方式
最佳實踐
Feign的繼承
關於繼承的爭議
- 官方觀點:官方不推薦使用
- 業界觀點:很多公司使用
- 個人觀點:權衡利弊
多參數請求構造
Get請求
TestController
@GetMapping("test-get")
public UserDTO query(UserDTO userDTO) {
return testUserCenterFeignClient.query(userDTO);
}
方法1
TestUserCenterFeignClient
import com.itmuch.contentcenter.domain.dto.user.UserDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "user-center")
public interface TestUserCenterFeignClient {
@GetMapping("/q")
UserDTO query(@SpringQueryMap UserDTO userDTO);
}
方法二
@FeignClient(name = "user-center")
public interface UserFeignClient {
@RequestMapping(value = "/q", method = RequestMethod.GET)
public UserDTO query(@RequestParam("id") Long id, @RequestParam("username") String username);
}
POST請求包含多個參數
下面來討論如何使用Feign構造包含多個參數的POST請求。假設服務提供者的Controller是這樣編寫的:
@RestController
public class UserController {
@PostMapping("/post")
public User post(@RequestBody User user) {
...
}
}
我們要如何使用Feign去請求呢?答案非常簡單,示例:
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/post", method = RequestMethod.POST)
public User post(@RequestBody User user);
}
Feign脫離Ribbon的使用
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
// 脫離ribbon的使用
@FeignClient(name = "baidu", url = "http://www.baidu.com")
public interface TestBaiduFeignClient {
@GetMapping("")
String index();
}
RestTemplate vs Feign
Feign性能優化
- 連接池[提升15%左右]
feign:
sentinel:
# 為feign整合sentinel
enabled: true
client:
config:
# 全局配置
default:
loggerLevel: full
requestInterceptors:
- com.itmuch.contentcenter.feignclient.interceptor.TokenRelayRequestIntecepor
httpclient:
# 讓feign使用apache httpclient做請求;而不是默認的urlconnection
enabled: true
# feign的最大連接數
max-connections: 200
# feign單個路徑的最大連接數
max-connections-per-route: 50
目前,在Spring cloud中服務之間通過restful方式調用有兩種方式
- restTemplate+Ribbon
- feign
從實踐上看,采用feign的方式更優雅(feign內部也使用了ribbon做負載均衡)。