SpringCloud 详解(七)

OpenFeign

Feign 是 SpringCloud 组件中的一个轻量级 RESTful 的 HTTP 服务客户端。Feign 内置了 Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign 的使用方式是:使用 Feign 的注解定义接口,调用这个接口,就可以调用服务注册中心的服务。

OpenFeign 是 SpringCloud 在 Feign 的基础上支持了 SpringMVC 的注解,如 @RequesMapping。OpenFeign 的 @FeignClient 可以解析 SpringMVC 的 @RequestMapping 注解下的接口,并通过动态代理的方式产生实现类,在实现类中做负载均衡并调用其它服务。

服务调用

准备工作

创建一个 Module,一个空的 Maven 项目。并导入下面的依赖。

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
32
33
34
35
36
37
38
39
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>ml.guest997</groupId>
<artifactId>Commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

在 resources 文件夹下创建 application.yaml 配置文件,并加入下面的配置。

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 80

spring:
application:
name: order

eureka:
client:
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/

主启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
package ml.guest997;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients //启动 Feign Clients
public class Order2 {
public static void main(String[] args) {
SpringApplication.run(Order2.class, args);
}
}

Service 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package ml.guest997.service;

import ml.guest997.pojo.CommonResult;
import ml.guest997.pojo.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;

@Component
@FeignClient("PAYMENT")
public interface PaymentService {
@GetMapping("/payment/add")
CommonResult add(Payment payment); //跟支付模块 controller 中定义的方式一样,不需要自己写实现类,通过动态代理实现。
}

Controller 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package ml.guest997.controller;

import lombok.extern.slf4j.Slf4j;
import ml.guest997.pojo.CommonResult;
import ml.guest997.pojo.Payment;
import ml.guest997.service.PaymentService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderController {
@Resource
private PaymentService paymentService;

@GetMapping("/consumer/payment/add")
public CommonResult add(Payment payment) {
return paymentService.add(payment);
}
}

测试

分别启动 Eureka-Server、Payment、Payment2 和 Order2 模块后,浏览器多次访问:127.0.0.1/consumer/payment/add?serial=Guest006,会发现端口是互相切换的,就说明 OpenFeign 内部已经实现了负载均衡。

超时控制

OpenFeign 调用远程服务时,一定时间内没有建立起请求或没有响应,就会报错。

添加支付 Controller

1
2
3
4
5
6
7
8
9
@GetMapping("/payment/timeout")
public String paymentTimeout() {
try {
TimeUnit.MINUTES.sleep(1); //休眠1分钟
} catch (InterruptedException e) {
e.printStackTrace();
}
return serverPort;
}

添加订单接口

1
2
@GetMapping("/payment/timeout")
String paymentTimeout();

添加订单 Controller

1
2
3
4
@GetMapping("/consumer/payment/timeout")
public String paymentFeignTimeout() {
return paymentService.paymentTimeout();
}

测试

分别启动 Eureka-Server、Payment、Payment2 和 Order2 模块后,浏览器访问:127.0.0.1/consumer/payment/timeout,过了一段时间后就会发现页面报错了。

添加配置

1
2
3
4
5
6
feign:
client:
config:
default:
connectTimeout: 3000 #指的是建立连接所用的时间
readTimeout: 80000 #指的是建立连接后收到响应所用的时间

日志增强

通过配置来调整日志级别,从而了解请求中的细节。

日志级别

  • NONE:默认的,不显示任何日志。
  • BASIC:仅记录请求方法、URL、响应状态码及执行时间。
  • HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息。
  • FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。

配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
package ml.guest997.config;

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

@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}

添加配置

1
2
3
logging:
level:
ml.guest997.service.PaymentService: debug #指定特定的接口的日志级别(会被上面的配置覆盖)

测试

分别启动 Eureka-Server、Payment、Payment2 和 Order2 模块后,浏览器访问:127.0.0.1/consumer/payment/add?serial=Guest007,会发现控制台的输出内容如下图所示。