openFeign的使用
1、openFeign是幹什麼的?
OpenFeign是一個顯示聲明式的WebService用戶端。使用OpenFeign能讓編寫Web Service用戶端更加簡單。使用時隻需定義服務接口,然後在上面添加注解。OpenFeign也支援可拔插式的編碼和解碼器。spring cloud對feign進行了封裝,使其支援MVC注解和HttpMessageConverts。和eureka(服務注冊中心)和ribbon組合可以實作負載均衡。在Spring Cloud中使用OpenFeign,可以做到使用HTTP請求通路遠端服務,就像調用本地方法一樣的,開發者完全感覺不到這是在調用遠端方法,更感覺不到在通路HTTP請求,非常的友善
2、建立服務子產品
建立order訂單子產品服務(之前文章已經建立了user服務),這樣兩個服務就可以測試feign接口了。
user服務的建立請看第一篇文章 https://blog.csdn.net/qq_38374397/article/details/125542389
建立maven子子產品
application.yml配置檔案
server:
port: 9091
spring:
application:
name: mdx-shop-order
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: mdx
group: mdx
bootstrap.properties配置檔案
spring.application.name=mdx-shop-order
spring.cloud.nacos.config.server-addr=localhost:8848
spring.cloud.nacos.config.extension-configs[0].data-id=mdx-shop-order.yaml
spring.cloud.nacos.config.extension-configs[0].group=shop
spring.cloud.nacos.config.extension-configs[0].refresh=true
spring.cloud.nacos.config.file-extension=yml
spring.cloud.nacos.config.namespace=mdx
spring.cloud.nacos.config.group=shop
3、引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
4、openFeign的使用
我們先建立一個common的公共子產品,然後引入一些工具類
在common中添加工具類,一些字元串操作和非空判斷
在其他子產品中引用common公共子產品
在order服務中先建立一個擷取訂單号的測試接口
建立一個controller層和service層,controller層代碼分别如下:
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("getOrderNo")
public String getOrderNo(String userId){
return orderService.getOrderNo(userId);
}
}
service層實作類,目前沒有查DB,做了簡單的判斷
@Service
public class OrderServiceImpl implements OrderService {
@Override
public String getOrderNo(String userId) {
if (StringUtils.isNotEmpty(userId) && userId.equals("mdx123456")){
return "O111222333444";
}
return "訂單不存在";
}
}
在啟動類開啟支援feign的遠端調用的注解 @EnableFeignClients
@SpringBootApplication
@EnableFeignClients
public class MdxShopUserApplication {
public static void main(String[] args) {
SpringApplication.run(MdxShopUserApplication.class, args);
}
}
然後在user服務中建立一個OrderFeign接口用來調用order服務
@FeignClient(value = "mdx-shop-order")
@Component
public interface OrderFeign {
@GetMapping("order/getOrderNo")
String getOrderNo(String userId);
}
@FeignClient标簽的常用屬性如下:
- name/value:指定FeignClient的名稱,如果項目使用了Ribbon,name屬性會作為微服務的名稱,用于服務發現
- url: url一般用于調試,可以手動指定@FeignClient調用的位址
- decode404:當發生http 404錯誤時,如果該字段位true,會調用decoder進行解碼,否則抛出FeignException
- configuration: Feign配置類,可以自定義Feign的Encoder、Decoder、LogLevel、Contract
- fallback: 定義容錯的處理類,當調用遠端接口失敗或逾時時,會調用對應接口的容錯邏輯,fallback指定的類必須實作@FeignClient标記的接口
- fallbackFactory: 工廠類,用于生成fallback類示例,通過這個屬性我們可以實作每個接口通用的容錯邏輯,減少重複的代碼
- path: 定義目前FeignClient的統一字首,當我們項目中配置了server.context-path,server.servlet-path時使用
value 屬性的值是order服務的服務名稱,也就是注冊到注冊中心中的服務名稱
我們在user服務中建立一個controller和service調用feign接口來測試一下
controller層
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("getOrderNo")
public String getOrderNo(String userId){
return userService.getOrderNo(userId);
}
}
service層
@Service
public class UserServiceImpl implements UserService {
@Autowired
private OrderFeign orderFeign;
@Override
public String getOrderNo(String userId) {
return orderFeign.getOrderNo(userId);
}
}
分别啟動user服務和order服務測試
啟動user服務的時候發現報錯了,是因為SpringCloud Feign在Hoxton.M2 RELEASED版本之後抛棄了Ribbon,使用了spring-cloud-loadbalancer,是以我們這裡還需要引入spring-cloud-loadbalancer的依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<version>3.1.1</version>
</dependency>
重新啟動項目,并檢視nacos,發現服務已經注冊成功
接下來通路user服務,看看是否能通過feign接口成功擷取到訂單号
user服務報錯
order服務提示資訊
通過意思資訊我們可以發現,我們的GET請求被當成了POST請求,如果不加預設的注解,Feign則會對參數預設加上@RequestBody注解,而RequestBody一定是包含在請求體中的,GET方式無法包含
我們改變一下feign接口,添加一個@RequestParam注解
@FeignClient(value = "mdx-shop-order")
@Component
public interface OrderFeign {
@GetMapping("order/getOrderNo")
String getOrderNo(@RequestParam String userId);
}
重新開機user服務,通路接口測試
成功擷取到order服務中的訂單号
5、openfeign接口添加請求頭資訊
我們有時候會在接口請求中傳遞一些頭資訊,來看一下feign是怎麼使用的
其中有5種方式可以實作傳遞請求頭資訊:
- 在@RequestMapping注解裡添加headers屬性
- 在方法參數前面添加@RequestHeader注解
- 在方法或者類上添加@Headers的注解
- 在方法參數前面添加@HeaderMap注解
- 實作RequestInterceptor接口
我們隻示範一個最簡單的,在方法參數前面添加@RequestHeader注解
修改一下OrderFeign接口
單個參數:
@GetMapping("order/getOrderNo")
String getOrderNo(@RequestParam String userId,@RequestParam String tenantId,@RequestHeader("Authorization") String token);
多個參數使用MultiValueMap
@GetMapping("order/getOrderNo")
String getOrderNo(@RequestParam String userId, @RequestParam String tenantId, @RequestHeader MultiValueMap<String, String> headers);
我們來測試下傳遞單個參數的情況
user服務代碼修改:
controller(添加HttpServletRequest參數):
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("getOrderNo")
public String getOrderNo(String userId,String tenantId,HttpServletRequest request){
return userService.getOrderNo(userId,tenantId,request);
}
}
service實作類(擷取request中的頭資訊并傳遞給feign接口)
@Service
public class UserServiceImpl implements UserService {
@Autowired
private OrderFeign orderFeign;
@Override
public String getOrderNo(String userId, String tenantId, HttpServletRequest request) {
return orderFeign.getOrderNo(userId,tenantId, request.getHeader("token"));
}
}
order服務:
controller(擷取頭資訊中的參數):
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("getOrderNo")
public String getOrderNo(String userId, String tenantId, HttpServletRequest request){
System.out.println("Authorization:" + request.getHeader("Authorization"));
return orderService.getOrderNo(userId,tenantId);
}
}
重新開機服務并調用接口
成功擷取到頭資訊
6、fallback的使用
來看一下fallback屬性的作用
fallback: 定義容錯的處理類,當調用遠端接口失敗或逾時時,會調用對應接口的容錯邏輯,fallback指定的類必須實作@FeignClient标記的接口
然後我們看下怎麼用
先定義一個容錯的處理類 OrderFeignHandler:
@Component
public class OrderFeignHandler implements OrderFeign {
@Override
public String getOrderNo(String userId, String tenantId, String token) {
String fallback = "目前人數過多,休息一會再試";
return fallback;
}
}
@FeignClient 注解添加fallback屬性 fallback = OrderFeignHandler.class
@FeignClient(value = "mdx-shop-order",fallback = OrderFeignHandler.class)
@Component
public interface OrderFeign {
@GetMapping("order/getOrderNo")
String getOrderNo(@RequestParam String userId,@RequestParam String tenantId,@RequestHeader("Authorization") String token);
}
重新開機user服務,先擷取一下正常的資料
正常傳回訂單号
我們在模拟一下異常的情況 修改order服務中的擷取單号接口,找不到單号則抛異常
@Service
public class OrderServiceImpl implements OrderService {
@Override
public String getOrderNo(String userId,String tenantId) {
System.out.println(tenantId);
if (StringUtils.isNotEmpty(userId) && userId.equals("mdx123456")){
return "O111222333444";
}else {
throw new RuntimeException("單号不存在");
}
}
}
重新開機order服務,參數中傳遞一個不存在的訂單号
發現并沒有傳回我們想要的資訊
檢查代碼,發現我們少了配置
添加sentinel,因為我們使用的是alibaba微服務體系,是以我們使用sentinel來做熔斷
sentinel 以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個次元保護服務的穩定性(sentinel如何使用我們後面的章節會講到)
添加sentinel 依賴
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
yml配置檔案添加feign開啟sentinel的配置
feign:
sentinel:
enabled: true
重新開機user服務,再次通路接口
我們可以看到order服務抛了訂單号不存在的異常
但是我們的接口成功傳回了容錯接口定義的資訊
到這裡feign的簡單使用已經全部結束了
創作不易,點個贊吧👍
最後的最後送大家一句話
白駒過隙,滄海桑田
與君共勉
上一篇文章
springcloud alibaba微服務 – nacos使用以及注冊中心和配置中心的應用(保姆級)
下一篇文章
springcloud alibaba微服務 – sentinel的使用(保姆級)
項目位址
mdx-shop gitee位址