一、为什么不使用ribbon而使用feign
1、feign更方便
首先看ribbon的导入依赖可以看出ribbon和eureka是来自同一个组织Netflix,所以在eureka中使用ribbon是很方便的。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring‐cloud‐starter‐netflix‐ribbon</artifactId>
</dependency>
但nacos比eureka好用许多,现在使用nacos的项目更多,早点的spring-cloud-alibaba版本也是将ribbon集成在自家的库里面的
但是较新版本的spring-cloud-alibaba已经舍弃了这个依赖
而且feign 默认集成了 Ribbon,默认实现了负载均衡,只需要创建一个接口并注解,就能很轻松的调用各服务提供的 HTTP 接口,明显更好用。
2、替代RestTemplate
RestTemplate 使用的是: spring-web 包下面的http 模块的http包中的API。 也就是Spring 自己封装的一套的httpclient API, 下面还是走java 的HttpurlConnection 建立连接然后传输数据。从名字也可以看出其是作为一个模板类可以在项目中使用。类似的还有RedisTemplate 等工具类。可以单独使用,单独使用的时候相当于直接连接到url, 不走微服务里面的服务发现以及负载均衡等机制。
ribbon的远程调用需要结合RestTemplate,加上@LoadBalanced才能使用,在创建模板时可读性差,参数复杂。
String url = "http://userservice/user/" + id;
User user = restTemplate.getForObject(url,User.class)
而feign就可以简化这一步骤,以接口的形式不仅可读性高,还可以重复使用
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id})
User findById(@PathVariable("id) Long id);
}
3、ribbon已经停止维护
现在ribbon已经从springcloud的官方组件中移除,springcloud推荐使用Spring Cloud Loadbalancer来处理负载均衡
二、远程服务调用
1、创建需要远程服务的实例
user-service负责查用户信息
如图所示服务创建成功
2、引入依赖
order-service需要查用户信息,在pom文件中引入feign的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
因为新版本的openfeign不包含loadbalanced,所以要引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
3、服务调用
声明客户端接口
使用客户端接口获取数据
如图所示访问成功
三、负载均衡配置
首先给user-service加入一些输出信息方便观看
调用三次请求
发现userservice1 、userservice2、userservice3分别调用了一次,是自带以轮询方式负载均衡的
1、创建配置类
如图所示我创建了两个负载均衡方式,随机和轮询
随机的
@SpringBootApplication
@LoadBalancerClient(name = "userservice",configuration = RandomLoadBalancerConfig.class)
public class OrderService1Application {
public static void main(String[] args) {
SpringApplication.run(OrderService1Application.class,args);
}
}
轮询的
public class RoundLoadBalancerConfig {
@Bean
ReactorLoadBalancer<ServiceInstance> roundLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RoundRobinLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
2、修改启动类
到启动类配置以随机方式负载均衡到userservice
发送10次请求,查看结果
四、feign自定义配置
1、以yml文件方式配置
feign:
client:
config:
user-service: #这里如果是default则是默认配置,如果是服务名则只是针对改服务
logger-level: FULL #包括NONE、BASIC、HEADERS、FULL四种
decoder: #把查询到的数据解码
encoder: #请求参数编码,通过http请求发送
contract: #支持的注解格式,默认是SpringMVC的注解
retryer: #失败重试机制,默认没有,但集成的ribbon中有,所以相当于有
2、以java代码方式配置
先注入bean,这里可以注入多个bean
public class FeignClientConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC;
}
}
而后如果全局配置,则放到@EnableClientConfiguration这个注解中
@EnableClientConfiguration(defaultConfiguration = FeignClientConfiguration.class)
如果局部配置,则放到@FeignClient这个注解中
@FeignClient(value = "userservice",configuration = FeignClientConfiguration.class)
五、feign的性能优化
feign是一个申明式客户端,只负责把申明变成请求,最终发送请求用的是别的客户端,feign的底层客户端实现有三种
URLConnection: //默认实现,不支持连接池
Apache HttpClient: //支持连接池
OKHttp: //支持连接池
建立连接池可以减少重复握手,因此需要切换客户端,两个带连接池的客户端依赖如下所示,引入其中一种即可。
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
httpclient的yml文件的配置如下
feign:
httpclient:
enabled: true #开启feign对httpclient的支持
max-connections: 200 #最大连接数
max-connections-per-route: 50 #每个路径的最大连接数
okhttp的yml文件配置貌似只用启用支持这一条
六、feign的最佳实践
1、以继承的方式优化
通过项目结构可以看出,order-service通过feign创建userclient去调用user-service服务
对比Userclient里的findUser接口和user-service里的user,发现两者的路由一样
其实是必须一样,否则该接口不能正确调用方法,但是这未免重复了,可以把这个请求路径剥离成UserApi,UserClient继承UserApi,UserController实现UserApi,这样的缺点是UserClient和UserController通过UserApi耦合在一起了,一方出问题,另外一方也会出问题。
2、以抽离的方式优化
order-service1调用user-service需要写一个userclient接口,order-service2调用user-service也需要写一个userclient接口,这就导致同样的代码写很多遍,不仅麻烦,还很容易出错,所以一种思路就是把userclient、orderclient等等都抽离到一个公共的feign-Api模块里去,把所用到的实体类都放到feign-Api里去,然后在自己的maven依赖中导入feign-Api,通过调用Feign-Api里的userclient、orderclient直接返回user、order对象。