Feign 基本操作

259 阅读3分钟

原有问题

@RestController
@RequestMapping("order")
public class OrderController {

   @Autowired
   private OrderService orderService;
    @Autowired
   private RestTemplate restTemplate;

    @GetMapping("{orderId}")
    public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
        // 根据id查询订单并返回
        Order order = orderService.queryOrderById(orderId);
        String url = "http://userservice/user/"+order.getUserId();
        User user = restTemplate.getForObject(url,User.class);
        order.setUser(user);
        return order;
    }
}

其中:

String url = "<http://userservice/user/"+order.getUserId()>;
User user = restTemplate.getForObject(url,User.class);

代码可读性差,编程体验不统一

参数复杂URL难以维护

Feign 概念

分布式http客户端、作用是帮助我们实现http请求的发送

原有问题改造

导入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

启动类添加注解@EnableFeignClients

image.png

相关代码

现有逻辑为:

订单模块会调用用户的相关信息,对应业务为orderserivce调用userseivice方法

graph TD
orderserivce --> userseivice

而有了feign就可以把它作为一个中介

graph TD
orderserivce --> UserClient --> userseivice

被调用者相关配置 => UserService

@Slf4j  
@RestController  
@RequestMapping("/user")  
/*@RefreshScope*/  
public class UserController {  
  
@Autowired  
private UserService userService;  
  
/**  
* 路径: /user/110  
*  
* @param id 用户id  
* @return 用户  
*/  
@GetMapping("/{id}")  
public User queryById(@PathVariable("id") Long id
    ,@RequestHeader(value = "Truth",required = false) String truth) {  
    return userService.queryById(id);  
    }  
}

中介相关配置=>Feign

要求(Feign的)方法参数请求方式、地址被调用者(UserService)一致(复制粘贴的事)

    import cn.itcast.feign.pojo.User;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;

    @FeignClient("userservice")
    public interface UserClient {

        @GetMapping("/user/{id}")
        User findById(@PathVariable("id") Long id);
    }

image.png

其中@FeignClient("userservice")填充的值,为被调用者的application.yml中spring. application.name的值

image.png

调用者相关配置=>Order

OrderService调用feign客户端

import cn.itcast.feign.clients.UserClient;  
import cn.itcast.feign.pojo.User;  
import cn.itcast.order.mapper.OrderMapper;  
import cn.itcast.order.pojo.Order;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
  
import javax.annotation.Resource;  
  
@Service  
public class OrderService {  
  
@Resource  
private OrderMapper orderMapper;  
@Autowired  
private UserClient userClient;  
  
public Order queryOrderById(Long orderId) {  
    // 1.查询订单  
    Order order = orderMapper.findById(orderId);  
    // feign 远程调用  
    User user = userClient.findById(order.getUserId());  
    // 封装user 和order  
    order.setUser(user);  
    // 4.返回  
    return order;  
    }  
}

image.png

OrderController直接调用orderService层方法

@RestController
@RequestMapping("order")
public class OrderController {

   @Autowired
   private OrderService orderService;


    @GetMapping("{orderId}")
    public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
        // 根据id查询订单并返回
        Order order = orderService.queryOrderById(orderId);

        return order;
    }
}

image.png

调用者的启动类上添加注解

@EnableFeignClients(clients = UserClient.class, defaultConfiguration = DefaultFeignConfiguration.class)

@MapperScan("cn.itcast.order.mapper")  
@SpringBootApplication  
@EnableFeignClients(clients = UserClient.class, defaultConfiguration = DefaultFeignConfiguration.class)  
public class OrderApplication {  
  
public static void main(String[] args) {  
    SpringApplication.run(OrderApplication.class, args);  
}  
  
@Bean  
@LoadBalanced  
public RestTemplate restTemplate(){  
    return new RestTemplate();  
}  
/*  
@Bean  
public IRule randomRule(){  
    return new RandomRule();  
}*/  
}

image.png

自定义配置

image.png

类似案例

可以参考这篇:juejin.cn/post/725189…

配置日志

方式一

全局生效

对于一切访问的微服务都生效

feign:
  client:
    config:
      default:
        loggerLevel: FULL

image.png

局部生效

只针对某个微服务配置

feign:
  client:
    config:
      ueseservice:
        loggerLevel: FULL

image.png

方式二

申明一个bean对象

import feign.Logger;
import org.springframework.context.annotation.Bean;

public class DefaultFeignConfiguration {

    @Bean
    public Logger.Level logLevel(){
        return Logger.Level.BASIC;
    }
}

image.png

全局配置

修改启动器注解:@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)

image.png

局部生效

修改启动器注解:@EnableFeignClients(value = "userservice", defaultConfiguration = DefaultFeignConfiguration.class)

性能调优

feign客户端实现方式:

URLConnection : 默认实现,不支持连接池

Apache HttpClient : 支持连接池

OKHttp : 支持连接池

因此使用带连接池的替代默认URLConnection

连接池配置

导入依赖

<!--HttpClient 依赖 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

配置连接池

feign:
  httpclient:
    enabled: true
    max-connections: 200
    max-connections-per-route: 50

image.png

ps : 日志级别,最好使用basic或none

实践方案

方法一

继承:给消费者的FeignClient和提供者的controller定义统一的父接口作为标准

方法二

抽取:将FeignClient抽离为独立模块,并将接口有关的POJO,默认的Feign配置都放到这个模块中,提供给所有消费者使用

新建feign-api模块,引入starter依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

image.png

复制UserClient,User,Congifuration到feign-api项目中

image.png

在调用者order-service的依赖中引入feigin-api

<!--引入feign的统一api-->
<dependency>
    <groupId>cn.itcast.demo</groupId>
    <artifactId>feign-api</artifactId>
    <version>1.0</version>
</dependency>

image.png

修改上述三个组件有关的import部分,改成导入feigin-api中的包

image.png

image.png