RestTemplate远程调用存在的问题

// 将localhost:8081修改为服务的名称即: userservice
User user = restTemplate.getForObject("http://userservice/user/"+order.getUserId(), User.class);
order.setUser(user);

存在的问题:

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

2、参数复杂URL难以维护

Feign简介

Feign是一个声明式的httpf服务端,Feign官方地址

其作用就是帮助我们优雅的实现http请求的发送,解决RestTemplate远程调用存在的问题

Feign的使用

1、引入Feign的依赖

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

2、开启自动装配功能

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

3、编写Feign客户端做接口声明

// 服务名称
@FeignClient("userservice")
public interface UserClient {
    // 请求类型:GET
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

4、在service中调用Feign接口

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private UserClient userClient;

    public Order queryOrderById(Long orderId) {
        Order order = orderMapper.findById(orderId);
        // 使用feign进行远程调用
        User user = userClient.findById(order.getUserId());
        order.setUser(user);
        // 4.返回
        return order;
    }
}

Feign集成了Ribbon自动实现了负载均衡

Feign的自定义配置

Feign运行自定义配置来覆盖默认配置,可以修改的配置如下:

类型

作用

说明

feign.Logger.Level

修改日志级别

包含四种不同的级别:NONE、BASIC、HEADERS、FULL

feign.codec.Decoder

响应结果的解析器

http远程调用的结果做解析,例如请求解析json字符串转为java对象

feign.codec.Encoder

请求参数编码

将请求参数编码,便于通过http请求发送

feign.Contract

支持的注解格式

默认是SpringMVC的注解

feign.Retryer

失败重试机制

请求失败的重试机制,默认是没有,不过会使用Ribbon的重试机制

一般需要修改的是日志级别

Feign修改日志

1、方式1:配置文件的方式

全局生效

feign:
  client:
    config:
      default:
        loggerLevel: FULL #feign的日志级别

局部生效

feign:
  client:
    config:
      userservice:    # 服务名称
        loggerLevel: FULL #日志级别

2、方式2:使用Java代码

需要先说明一个Bean

public class FeignClientConfiguration {
    @Bean
    public Logger.Level feignLogLevel() {
        return Logger.Level.FULL;
    }
}

注意:没有@Configuration注解

如果是全局配置,则把他放到@EnableFeignClients这个注解中

@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)

如果是局部配置,则把他放到@FeignClient这个注解中

@FeignClient(value="userservice",configuration=FeignClientConfiguration.class)

Feign性能优化

Feign底层的客户端实现:

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

2、Apache HttpClient:支持连接池

3、OKHttp:支持连接池

日志级别,最好用basic或none

Feign连接池配置

1、引入HttpClient依赖

<dependency>
     <groupId>io.github.openfeign</groupId>
     <artifactId>feign-httpclient</artifactId>
</dependency>

2、配置连接池

feign:
  client:
    config:
      default:
         loggerLevel: none
  httpclient:
    enabled: true #开启feign对httpclient的支持
    max-connections: 200 #最大连接数
    max-connections-per-route: 50 #每个路径的最大连接数

Feign使用的优化

1、在父工程中添加一个Maven模块,将feign相关的config、pojo、client放到这个模块中,如图

springboot feign调用异常处理 springboot feign远程调用_feign

注意:这里需要引入Feign相关依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

然后使用maven进行打包,打包后在需要使用的maven项目中的pom文件进行引用

<dependency>
    <groupId>cn.itcast.demo</groupId>
    <artifactId>feign-api</artifactId>
    <version>1.0</version>
</dependency>

在启动类中的@EnableFeignClients指定引入的Client

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
// clients指定对应的client
@EnableFeignClients(clients = {UserClient.class},defaultConfiguration = FeignClientConfiguration.class)
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

也可以扫描Feign模块的Client所在的包

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
// basePackages指定client所在的包
@EnableFeignClients(basePackages = "cn.itcase.feign.clients",defaultConfiguration = FeignClientConfiguration.class)
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}