天天看點

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

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子子產品

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)
SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

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的公共子產品,然後引入一些工具類

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

在common中添加工具類,一些字元串操作和非空判斷

在其他子產品中引用common公共子產品

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

在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服務

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)
@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服務測試

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

啟動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,發現服務已經注冊成功

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

接下來通路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服務中的訂單号

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

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);
    }
}
           

重新開機服務并調用接口

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

成功擷取到頭資訊

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

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服務,先擷取一下正常的資料

正常傳回訂單号

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

我們在模拟一下異常的情況 修改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服務,參數中傳遞一個不存在的訂單号

發現并沒有傳回我們想要的資訊

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

檢查代碼,發現我們少了配置

添加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服務抛了訂單号不存在的異常

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

但是我們的接口成功傳回了容錯接口定義的資訊

SpringCloud Alibaba微服務 -- OpenFeign的使用(保姆級)

到這裡feign的簡單使用已經全部結束了

創作不易,點個贊吧👍

最後的最後送大家一句話

白駒過隙,滄海桑田

與君共勉

上一篇文章

springcloud alibaba微服務 – nacos使用以及注冊中心和配置中心的應用(保姆級)

下一篇文章

springcloud alibaba微服務 – sentinel的使用(保姆級)

項目位址

mdx-shop gitee位址

繼續閱讀