欢迎光临
我们一直在努力

OpenFeign

目录

Imperative REST Client vs Declarative REST Client

命令式 REST 客户端(Imperative REST Client)

声明式 REST客户端 (Declarative REST Client)

常用注解

OpenFeign-远程调用-声明式实现

调用流程

实现流程

OpenFeign-远程调用-第三方API

实现步骤

客户端负载均衡和服务端负载均衡的区别?

OpenFeign-配置进阶-日志

OpenFeign-配置进阶-超时控制

OpenFeign-配置进阶-重试机制

OpenFeign-配置进阶-拦截器

OpenFeign-配置进阶-Fallback

总结

OpenFeign 是 Spring Cloud 生态中的声明式 HTTP 客户端,核心作用是简化微服务间的远程调用—— 无需手动通过 RestTemplate 拼接 URL、处理参数和响应转换,只需通过「接口 + 注解」的方式,就能像调用本地方法一样调用远程服务接口。

Imperative REST Client vs Declarative REST Client

命令式 REST 客户端(Imperative REST Client)

回顾之前用的 RestTemplate、Java 原生 HttpURLConnection、Apache HttpClient 都属于这类。

如下例,你必须手动处理「获取服务实例、拼接 URL、发送 GET 请求、将 JSON 转为 Product 对象」这些步骤,这就是「命令式」。

// 命令式:每一步都要手动实现(服务发现→拼接URL→发送请求→转换响应)
List<ServiceInstance> instances = discoveryClient.getInstances("server-product");
ServiceInstance instance = instances.get(0);
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/product/" + productId;
Product product = restTemplate.getForObject(url, Product.class);

声明式 REST客户端 (Declarative REST Client)

它是一种软件设计风格 / 模式 ,而 OpenFeign 就是这种模式的典型实现(具体框架)。无需编写实现细节,只需通过「接口 + 注解」声明你的需求,框架会自动完成底层实现。

如下例,只需要通过「接口 + 注解」声明「我要调用哪个服务的哪个接口,传入什么参数,返回什么结果」,至于「如何发现服务、如何拼接 URL、如何发送请求、如何转换响应」,这些底层细节都由 OpenFeign 框架自动完成,这就是「声明式」。

// 1. 声明需求:我要调用 server-product 服务的 /product/{id} 接口(GET 请求)
@FeignClient(name = "server-product")
public interface ProductFeignClient {
@GetMapping("/product/{id}")
Product getProductById(@PathVariable("id") Long id);
}

// 2. 直接使用:无需关心底层如何实现
Product product = productFeignClient.getProductById(productId);

常用注解

  • 指定远程地址:@Feignclient
  • 指定请求方式:@GetMapping、@PostMapping、@DeleteMapping…
  • 指定携带数据:@RequestHeader、@RequestParam、@RequestBody…
  • 指定结果返回:响应模型
  • OpenFeign-远程调用-声明式实现

    调用流程

    实现流程

    1.在services的pom文件引入依赖:

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    2.在service-order的主程序加上@EnableFeignClients注解,开启feign远程调用功能

    package com.atguigu.order;

    import com.alibaba.cloud.nacos.NacosConfigManager;
    import com.alibaba.nacos.api.config.ConfigService;
    import com.alibaba.nacos.api.config.listener.Listener;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    import org.springframework.context.annotation.Bean;

    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;

    @SpringBootApplication
    @EnableDiscoveryClient//开启服务发现功能
    @EnableFeignClients//开启feign远程调用功能
    public class OrderMainApplication {
    public static void main(String[] args) {
    SpringApplication.run(OrderMainApplication.class, args);
    }

    //1.项目启动就监听配置文件变化
    //2.发生变化后拿到变化值
    //3.发送邮件

    @Bean
    ApplicationRunner applicationRunner(NacosConfigManager nacosConfigManager) {
    return args -> {
    ConfigService configService = nacosConfigManager.getConfigService();
    configService.addListener("service-order.properties", "DEFAULT_GROUP", new Listener() {
    @Override
    public Executor getExecutor() {
    return Executors.newFixedThreadPool(4);
    }

    @Override
    public void receiveConfigInfo(String configInfo) {
    System.out.println("变化的配置项:"+ configInfo);
    System.out.println("发送邮件");
    }
    });
    System.out.println("==============");
    };
    }
    }

    3.在service-order下新建一个类,并加上@FeignClient,表明是基于Feign实现的远程调用客户端。

    package com.atguigu.order.feign;

    import org.springframework.cloud.openfeign.FeignClient;

    @FeignClient//声明这个类是一个FeignClient
    public class ProductFeignClient {

    }

    按上述配置完成之后,就可以具体实现要远程调用的逻辑了。

    实现根据商品id远程调用商品信息:

    1.在上述步骤3类中加入远程调用逻辑:

    package com.atguigu.order.feign;

    import com.atguigu.product.bean.Product;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestHeader;

    @FeignClient(value = "service-product")
    //声明这个类是一个FeignClient,value值表示要远程调用微服务的名字
    public interface ProductFeignClient {

    //MVC注解的两套使用逻辑:
    //1.标注在Controller上,表示接收这样的请求
    //2.标注在FeignClient上,表示发送这样的请求
    @GetMapping("/product/{productId}")
    Product getProductById(@PathVariable("productId") Long productId);
    }

    2.实现上述远程调用逻辑之后,在service-order的service实现类中,修改生成订单逻辑。代码如下:

    /**
    * 创建订单,并远程调用商品服务查询商品信息
    * @param productId
    * @param userId
    * @return
    */
    @Override
    public Order createOrder(Long productId, Long userId) {
    Order order = new Order();
    // Product product = getProductFromRemoteWithLoadBalancer(productId);
    Product product = productFeignClient.getProductById(productId);
    order.setId(1L);
    //总金额
    order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum())));
    order.setUserId(userId);
    order.setNickName("lqq");
    order.setAddress("xian");
    //商品列表
    order.setProductlist(Arrays.asList( product));

    return order;
    }

    记住要注入ProductFeignClient对象:

    @Autowired
    ProductFeignClient productFeignClient;

    3.重启项目,前后端测试结果如下:

    OpenFeign-远程调用-第三方API

    第三方调用时,我们可以查阅官方文档,直接获取其调用地址,不需要在注册中心查询什么微服务需要调用什么注册地址。OpenFeign也能做到这个功能。

    比如调用墨迹天气:

    通过查询接口文档可以获得上述信息。

    实现步骤

    1.在feign下新建一个类:

    package com.atguigu.order.feign;

    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestHeader;
    import org.springframework.web.bind.annotation.RequestParam;

    @FeignClient(value="weather-client", url = "http://aliv18.data.moji.com")
    public interface WeatherFeignClient {

    @PostMapping("/whapi/json/alicityweather/condition")
    String getWeather(@RequestHeader("Authorization") String auth,
    @RequestParam("token") String token,
    @RequestParam("cityId") String cityId);
    }

    2.在单元测试下编写一个测试类,来测试该方法是否正确。

    package com.atguigu.order;

    import com.atguigu.order.feign.WeatherFeignClient;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.test.context.SpringBootTest;

    @SpringBootTest
    public class WeatherTest {

    @Autowired
    WeatherFeignClient weatherFeignClient;

    @Test
    void test(){
    String weather = weatherFeignClient.getWeather("93b7e19861a24c519a7548b17dc16d75",
    "50b53ff8dd7d9fa320d3d3ca32cf8ed1", "2182");
    System.out.println("weather:" + weather);
    }
    }

    3.测试通过。

    客户端负载均衡和服务端负载均衡的区别?

    OpenFeign-配置进阶-日志

    1.在yml文件里添加:

    logging:
    level:
    com.atguigu.order.feign: debug

    2.在配置类里添加:

    @Bean
    Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL;
    }

    配置完成之后,就可以在日志中看到feign远程调用的全部过程。

    OpenFeign-配置进阶-超时控制

    超时等待->导致雪崩。

    远程调用结果:正确结果、错误信息、兜底数据。

    两种超时时间:

    • connectTimeout连接超时
    • readTimeout读取超时

    调用过程:

    其中connectTimeout连接超时就是指1.建立连接的时间,readTimeout读取超时就是指2、3、4这个过程的时间。

    在OpenFeign中的默认值是:

    创建订单时,A向B建立连接,我们将B睡眠100s,在B的sevice实现类中加入以下代码,在前端请求生成订单时,返回连接超时。

    try {
    TimeUnit.SECONDS.sleep(100);
    } catch (InterruptedException e) {
    throw new RuntimeException(e);
    }
    return product;

    以上是单独在某个微服务的实现类对连接超时时间进行配置,接下来使用yml文件实现对多个客户端的读取超时时间进行配置:

    spring:
    cloud:
    feign: # 关键:添加 feign 层级,不能少
    client:
    config:
    default: # 全局生效(所有 Feign 客户端)
    logger-level: full # 日志级别 full(打印完整请求/响应)
    connect-timeout: 1000 # 连接超时 1 秒(也可以写 connectTimeout)
    read-timeout: 2000 # 读取超时 2 秒(也可以写 readTimeout)
    service-product: # 针对 service-product 服务生效(优先级高于全局)
    logger-level: full
    connect-timeout: 3000 # 连接超时 3 秒
    read-timeout: 5000 # 读取超时 5 秒

    OpenFeign-配置进阶-重试机制

    远程调用超时失败后,还可以进行多次尝试,如果某次成功返回ok,如果多次依然失败则结束调用,返回错误。

    OpenFeign底层默认是从不重试。

    我们可以配置重试策略:

    方式一:在applicaton-feign.yml添加:

    retryer: feign.retryer.Default

    方式二:

    查看官方文档:

    在OrderConfig中,添加:

    @Bean
    Retryer retryer() {
    return new Retryer.Default();
    }

    项目启动后,前后端联调测试,发现订单发送五次请求之后返回错误信息。

    OpenFeign-配置进阶-拦截器

    响应拦截器用的不多,现在以请求拦截器为例:

    OpenFeign在远程调用之前,想要调用请求拦截器进行拦截,首先编写一个拦截器:

    1.在com.atguigu.order下创建一个interceptor.TokenRequestInterceptor类:

    package com.atguigu.order.interceptor;

    import feign.RequestInterceptor;
    import feign.RequestTemplate;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;

    import java.util.UUID;

    @Component
    public class TokenRequestInterceptor implements RequestInterceptor {

    /**
    * 拦截请求,给请求头添加token
    * requestTemplate:表示这次请求的详细信息封装到了RequestTemplate请求模板里
    * @param requestTemplate 请求模板
    */
    @Override
    public void apply(RequestTemplate requestTemplate) {
    System.out.println("拦截请求,给请求头添加token");
    requestTemplate.header("token", UUID.randomUUID().toString());

    }
    }

    2.在商品服务的controller中,将收到的token打印出来:

    /**
    * 查询商品信息
    * @param productId
    */
    @GetMapping("/product/{productId}")
    public Product getProduct(@PathVariable Long productId,
    HttpServletRequest request) {
    String token = request.getHeader("token");
    System.out.println("查询商品信息,productId = " + productId );
    System.out.println("token = " + token);
    return productService.getProduct(productId);
    }

    3.启动项目,查看商品的日志信息,查看是否打印出相应的token的信息:

    OpenFeign-配置进阶-Fallback

    Fallback:兜底返回

    注意:此功能需要整合Sentinel才能实现

    兜底数据:默认数据、缓存数据、假数据….

    1.在订单服务下的feign新建一个fallback.ProductFeignClientFallback类:

    package com.atguigu.order.feign.fallback;
    import java.math.BigDecimal;

    import com.atguigu.order.feign.ProductFeignClient;
    import com.atguigu.product.bean.Product;
    import org.springframework.stereotype.Component;

    @Component
    public class ProductFeignClientFallback implements ProductFeignClient {
    @Override
    public Product getProductById(Long productId) {
    /**
    * 只有远程服务调用失败,才会进入此方法
    */
    System.out.println("进入兜底回调…");
    Product product = new Product();
    product.setId(productId);
    product.setPrice(new BigDecimal("0"));
    product.setProductname("未知商品");
    product.setNum(0);

    return product;
    }
    }

    2.在ProductFeignClient上加上注解

    @FeignClient(value = "service-product", fallback = ProductFeignClientFallback.class)

    3.在service-order的pom文件中导入sentinel依赖

    <!– 导入sentinel依赖–>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

    4.在application-feign.yml文件中配置sentinel

    feign:
    sentinel:
    enabled: true

    5.启动项目,前后端联调测试发生在商品服务宕机时进入兜底回调:

    总结

    1.熟练编写 OpenFeign远程调用客户端

    2.熟练配置 OpenFeign客户端属性

    • 连接超时
    • 读取超时

    3.掌握 拦截器 用法

    4.掌握 Fallback兜底返回机制及用法

     

    赞(0)
    未经允许不得转载:171主机测评 » OpenFeign
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址