SpringCloud - Ribbon和Feign
# Ribbon
# 对比
RPC
- 远程过程调用,像调用本地方法一样调用服务器的服务
- 支持同步或异步
- 客户端和服务器之间建立 TCP 连接,可以一次建立一个,也可以多个调用复用一次连接
- RPC 数据包小 (谷歌 protobuf 以二进制方式传输)
- 比较复杂要进行:编码,解码,序列化,连接,丢包,拆包,组合,协议制定
Rest (HTTP) - HTTP 请求,支持多种协议和功能
- 开发方便成本低
- http 数据包大
- java 开发:HttpClient URLConnection
# 开发
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
1
2
3
4
2
3
4
在 application 种配置
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
1
2
3
4
5
2
3
4
5
修负载均衡改策略
/**
* 功能描述: 负载均衡策略
* RoundRobinRule:轮询
* RandomRule:随机
* AvailabilityFilteringRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,以及并发的连接数量
* 超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;
* WeightedResponseTimeRule: 根据平均响应时间计算所有服务的权重,响应时间越快,服务权重越大,被选中的机率越高;
* 刚启动时,如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够时,会切换到WeightedResponseTimeRule
* RetryRule: 先按照RoundRobinRule的策略获取服务,如果获取服务失败,则在指定时间内会进行重试,获取可用的服务;
* BestAvailableRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
* ZoneAvoidanceRule: 默认规则,复合判断server所在区域的性能和server的可用性选择服务器;
* @return : com.netflix.loadbalancer.IRule
* @author : big uncle
* @date : 2019/9/7 14:07
*/
@Bean
public IRule myRule(){
//自定义均衡策略
return new RandomRule();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
实际调用
/**
* 订单消费服务
* @author shengwu ni
*/
@RestController
@RequestMapping("/consumer/order")
public class OrderConsumerController {
/**
* 订单服务提供者模块的 url 前缀
*/
// private static final String ORDER_PROVIDER_URL_PREFIX = "http://localhost:8001";
private static final String ORDER_PROVIDER_URL_PREFIX = "http://MICROSERVICE-ORDER";
@Resource
private RestTemplate restTemplate;
@GetMapping("/get/{id}")
public TOrder getOrder(@PathVariable Long id) {
return restTemplate.getForObject(ORDER_PROVIDER_URL_PREFIX + "/provider/order/get/" + id, TOrder.class);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Feign
# fegin 简介
fegin 是 Netflix 开发的声明式、模板化的 HTTP 客户端,其令该来自 Retrofit、JAXRS-2.0 以及 WebSocket。feign 可帮助我们更加便捷,优雅的调用 HTTP API。
在 spring cloud 中,使用 feign 非常简单,创建一个接口,并在接口上添加一些注解,代码就完成了。feign 支持多种注解,例如 feign 自带的注解或者 JAX-RS 注解等 。
spring cloud 对 feign 进行了增强,使 feign 支持了 Spring MVC 注解,并整合了 Ribbon 和 Eureka
, 从而让 Feign 的使用更加便捷。
# 使用
添加依赖
<!-- 包含了Ribbon 和 hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1
2
3
4
5
2
3
4
5
编写 接口,path 是请求控制器的路径,name 是访问服务的 application.name
// name=eureka里注册的服务
@FeignClient(path = "test", name = "client-hyq-life-server")
public interface TestApi {
@RequestMapping(value = "/idnex", method = RequestMethod.GET)
String idnex();
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
编写实现
@RestController
@RequestMapping("test")
public class TestController implements TestApi {
@Autowired
private TestServer testServer;
@Override
public String idnex(){
return testServer.idnex();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
编写调用
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private TestApi testApi;
@GetMapping("/index")
public String index(){
return testApi.idnex();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
如果两个服务是分离的都需要给启动类加
@EnableFeignClients
@SpringBootApplication
public class BffAppServerApplication {
public static void main(String[] args) {
SpringApplication.run(BffAppServerApplication.class, args);
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# Feign 对压缩的支持
feign:
# 不使用 openfeign 自带的熔断
hystrix:
enabled: false
compression:
#配置请求 GZIP 压缩
request:
enabled: true
#配置压缩支持的 MIME TYPE
mime-types: text/xml,application/xml,application/json
#配置压缩数据大小的最小阀值,只有超过了这个大小的请求才会对其进行压缩。,默认 2048
min-request-size: 300
#配置响应 GZIP 压缩
response:
enabled: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# feign 对 OkHttp 的支持
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
拦截日志,这里建议做的有意义一些。
/**
* @author big uncle
* @date 2020/6/17 9:02
* ok http 拦截日志
**/
@Slf4j
public class OkHttpLogInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
log.debug("OkHttpUrl : " + chain.request().url());
return chain.proceed(chain.request());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
配置
/**
* @author big uncle
* @date 2020/6/17 8:58
**/
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {
@Bean
public okhttp3.OkHttpClient okHttpClient(){
return new okhttp3.OkHttpClient.Builder()
// 读取超时设置
.readTimeout(60, TimeUnit.SECONDS)
// 连接超时设置
.connectTimeout(60, TimeUnit.SECONDS)
//
.writeTimeout(120, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool())
.addInterceptor(okHttpLogInterceptor())
// .addInterceptor();
.build();
}
@Bean
public OkHttpLogInterceptor okHttpLogInterceptor(){
return new OkHttpLogInterceptor();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
配置出现问题,发现配置的超时时间一直不生效,我读取时间设置 30 秒,在拦截里面看只有 1 秒,所以很多时候提示超时。
最后有个大神跟我说了,fegin 一直都是用的是 default 配置,让我在 yml 配置 connectTimeout,readTimeout
feign:
compression:
# 请求压缩
request:
enabled: true
mime-types: "text/xml,application/xml,application/json"
# 用于设置请求的最小阈值
min-request-size: 1024
# 响应压缩
response:
enabled: true
hystrix:
enabled: false
okhttp:
enabled: true
client:
config:
default:
connectTimeout: 30000
readTimeout: 40000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
经过发现的确可以做到,在代码里的配置优先级居然没有配置文件高,我个人觉得是有问题的,忽然又想到那配置的池是否也是无用的,待观察。
# 日志
这里为了看每个请求的路径,参数,耗时我们可以进行如下操作,loggerLevel:
feign:
compression:
# 请求压缩
request:
enabled: true
mime-types: "text/xml,application/xml,application/json"
# 用于设置请求的最小阈值
min-request-size: 1024
# 响应压缩
response:
enabled: true
hystrix:
enabled: false
okhttp:
enabled: true
client:
config:
default:
connectTimeout: 30000
readTimeout: 40000
# BASIC FULL HEADERS NONE
loggerLevel: BASIC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
BASIC 打印如下
2020-06-18 09:45:51.670 DEBUG 26772 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] ---> POST http://server-data-platform/menu/getSonDataMenuList HTTP/1.1
2020-06-18 09:45:52.637 DEBUG 26772 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] <--- HTTP/1.1 200 (966ms)
2020-06-18 09:45:52.767 DEBUG 26772 --- [nio-8888-exec-2] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] ---> POST http://server-data-platform/electricityStation/getElectricityStationList HTTP/1.1
2020-06-18 09:45:52.805 DEBUG 26772 --- [nio-8888-exec-2] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] <--- HTTP/1.1 200 (38ms)
1
2
3
4
5
2
3
4
5
FULL 打印如下
2020-06-18 09:54:05.190 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] ---> POST http://server-data-platform/menu/getSonDataMenuList HTTP/1.1
2020-06-18 09:54:05.191 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] Content-Length: 1733
2020-06-18 09:54:05.191 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] Content-Type: application/json
2020-06-18 09:54:05.191 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList]
2020-06-18 09:54:05.191 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] {"data":{"id":null,"parentId":null,"menuName":null,"menuPath":"/electricityStation/list","menuOrder":null,"icon":null,"remarks":null,"menuData":null,"menuButton":null},"token":"5a717dca1142d685c8aa54b45d0388c8","user":{"id":1,"account":"15771720565","name":"冯谦润","phone":"15771720565","onlineState":null,"remarks":"fdsafdsa","rolesId":1,"avatar":null,"token":"5a717dca1142d685c8aa54b45d0388c8","roles":{"id":1,"roleName":"超级管理员","remarks":"2222","menus":null},"menus":[{"id":1,"parentId":0,"menuName":"大屏展示","menuPath":"/dashboard","menuOrder":1000,"icon":"el-icon-monitor","remarks":null,"menuData":false,"menuButton":false,"child":[]},{"id":2,"parentId":0,"menuName":"数据分析","menuPath":"/dataAnalysis","menuOrder":2000,"icon":"el-icon-data-line","remarks":null,"menuData":false,"menuButton":false,"child":[]},{"id":3,"parentId":0,"menuName":"系统管理","menuPath":"/setting","menuOrder":3000,"icon":"el-icon-setting","remarks":null,"menuData":false,"menuButton":false,"child":[{"id":5,"parentId":3,"menuName":"角色管理","menuPath":"/setting/roles","menuOrder":3200,"icon":null,"remarks":null,"menuData":false,"menuButton":false,"child":[]},{"id":6,"parentId":3,"menuName":"菜单管理","menuPath":"/setting/menus","menuOrder":3300,"icon":null,"remarks":null,"menuData":false,"menuButton":false,"child":[]}]},{"id":16,"parentId":0,"menuName":"电站管理","menuPath":"/electricityStation","menuOrder":4000,"icon":"el-icon-lightning","remarks":null,"menuData":false,"menuButton":false,"child":[{"id":17,"parentId":16,"menuName":"电站信息","menuPath":"/electricityStation/list","menuOrder":4100,"icon":null,"remarks":"列出所有电站","menuData":false,"menuButton":false,"child":[]}]}]}}
2020-06-18 09:54:05.191 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] ---> END HTTP (1733-byte body)
2020-06-18 09:54:06.014 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] <--- HTTP/1.1 200 (822ms)
2020-06-18 09:54:06.015 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] connection: keep-alive
2020-06-18 09:54:06.015 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] content-type: application/json
2020-06-18 09:54:06.015 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] date: Thu, 18 Jun 2020 01:54:05 GMT
2020-06-18 09:54:06.015 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] keep-alive: timeout=60
2020-06-18 09:54:06.015 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] transfer-encoding: chunked
2020-06-18 09:54:06.015 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList]
2020-06-18 09:54:06.017 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] {"code":200,"msg":null,"data":[{"id":24,"parentId":17,"menuName":"电站数据","menuPath":"/electricityStation/data","menuOrder":4110,"icon":null,"remarks":null,"menuData":true,"menuButton":false,"child":[{"id":25,"parentId":24,"menuName":"平顶山","menuPath":"/electricityStation/data/pds","menuOrder":4111,"icon":null,"remarks":null,"menuData":true,"menuButton":false,"child":[]}]}]}
2020-06-18 09:54:06.017 DEBUG 29140 --- [nio-8888-exec-1] com.giant.cloud.api.MenuApi : [MenuApi#getSonDataMenuList] <--- END HTTP (388-byte body)
2020-06-18 09:54:06.453 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] ---> POST http://server-data-platform/electricityStation/getElectricityStationList HTTP/1.1
2020-06-18 09:54:06.453 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] Content-Length: 1756
2020-06-18 09:54:06.453 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] Content-Type: application/json
2020-06-18 09:54:06.453 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList]
2020-06-18 09:54:06.453 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] {"data":{"data":{"id":null,"parentId":null,"menuName":null,"menuPath":null,"menuOrder":null,"icon":null,"remarks":null,"menuData":null,"menuButton":null,"menuId":"25"},"current":1,"size":20},"token":"5a717dca1142d685c8aa54b45d0388c8","user":{"id":1,"account":"15771720565","name":"冯谦润","phone":"15771720565","onlineState":null,"remarks":"fdsafdsa","rolesId":1,"avatar":null,"token":"5a717dca1142d685c8aa54b45d0388c8","roles":{"id":1,"roleName":"超级管理员","remarks":"2222","menus":null},"menus":[{"id":1,"parentId":0,"menuName":"大屏展示","menuPath":"/dashboard","menuOrder":1000,"icon":"el-icon-monitor","remarks":null,"menuData":false,"menuButton":false,"child":[]},{"id":2,"parentId":0,"menuName":"数据分析","menuPath":"/dataAnalysis","menuOrder":2000,"icon":"el-icon-data-line","remarks":null,"menuData":false,"menuButton":false,"child":[]},{"id":3,"parentId":0,"menuName":"系统管理","menuPath":"/setting","menuOrder":3000,"icon":"el-icon-setting","remarks":null,"menuData":false,"menuButton":false,"child":[{"id":5,"parentId":3,"menuName":"角色管理","menuPath":"/setting/roles","menuOrder":3200,"icon":null,"remarks":null,"menuData":false,"menuButton":false,"child":[]},{"id":6,"parentId":3,"menuName":"菜单管理","menuPath":"/setting/menus","menuOrder":3300,"icon":null,"remarks":null,"menuData":false,"menuButton":false,"child":[]}]},{"id":16,"parentId":0,"menuName":"电站管理","menuPath":"/electricityStation","menuOrder":4000,"icon":"el-icon-lightning","remarks":null,"menuData":false,"menuButton":false,"child":[{"id":17,"parentId":16,"menuName":"电站信息","menuPath":"/electricityStation/list","menuOrder":4100,"icon":null,"remarks":"列出所有电站","menuData":false,"menuButton":false,"child":[]}]}]}}
2020-06-18 09:54:06.454 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] ---> END HTTP (1756-byte body)
2020-06-18 09:54:06.483 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] <--- HTTP/1.1 200 (29ms)
2020-06-18 09:54:06.484 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] connection: keep-alive
2020-06-18 09:54:06.484 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] content-type: application/json
2020-06-18 09:54:06.484 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] date: Thu, 18 Jun 2020 01:54:05 GMT
2020-06-18 09:54:06.484 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] keep-alive: timeout=60
2020-06-18 09:54:06.484 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] transfer-encoding: chunked
2020-06-18 09:54:06.484 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList]
2020-06-18 09:54:06.484 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] {"code":200,"msg":null,"data":{"current":1,"size":20,"total":1,"records":[{"createTime":"2020-06-15T03:46:17.000+00:00","updateTime":"2020-06-15T03:46:17.000+00:00","deleteFlag":false,"id":1,"menuId":25,"name":"平顶山新能源项目1期","buildingDegree":30,"cityName":"平顶山","cityId":"410400","lng":"113.308","lat":"33.7352","remarks":"测试数据"}]}}
2020-06-18 09:54:06.484 DEBUG 29140 --- [nio-8888-exec-3] c.giant.cloud.api.ElectricityStationApi : [ElectricityStationApi#getElectricityStationList] <--- END HTTP (362-byte body)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
我就试到这里了,正常来说 FULL 已经足够了。
# Feign 的全局异常处理
@Configuration
public class FeignClientErrorDecoder implements ErrorDecoder {
private static final Log log = LogFactory.getLog(FeignClientErrorDecoder.class);
@Override
public Exception decode(String methodKey, Response response) {
try {
String body = IoUtil.read(response.body().asInputStream(), "utf-8");
Map<String,String> errMap = JSONObject.parseObject(body, HashMap.class);
return new InternalException(errMap.get("message"));
}catch(Exception e){
return new InternalException(e.getMessage());
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
上次更新: 4/1/2025, 5:03:02 PM