Spring Cloud(08)——Feign服务接口调用
在Spring Cloud(07)——Ribbon负载均衡服务的介绍和使用中,我们使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对微服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。因此下面我们使用Feign来帮助我们定义和实现依赖服务接口的定义,就像Java程序员熟悉的面向接口的编程思想。
1、Feign简介
-
Feign 是一个声明式的 REST 客户端,它能让 REST 调用更加简单。Feign 提供了 HTTP 请求的模板,通过编写简单的接口和插入注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。
-
Feign可以帮助我们更快捷、优雅地调用HTTP API。
-
Feign 是一个使用起来更加方便的客户端,它让微服务之间的调用变得更简单了,类似controller调用service,用起来就好像调用本地方法一样,完全感觉不到是调用远程方法。
-
Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。
-
Feign 中集成了Ribbon
Feign与OpenFeign的区别
2、OpenFeign使用步骤
使用OpenFeign只需创建一个接口,并在接口上添加注解(@FeignClient
)即可。
1、创建cloud-consumer-feign-order80模块
2、导入pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.cheng.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</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>
</dependency>
</dependencies>
3、编写yml配置文件
server:
port: 80
eureka:
client:
register-with-eureka: false # 不向eureka注册中心注册自己
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ #Eureka集群
4、主启动类
@SpringBootApplication
@EnableFeignClients //开启feign的功能
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
5、新建PaymentFeignService接口,并添加@FeignClient注解,该接口用于定义并实现Feign的功能
package com.cheng.springcloud.service;
import com.cheng.springcloud.pojo.CommonResult;
import com.cheng.springcloud.pojo.Payment;
import feign.Param;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //绑定要访问的服务名
public interface PaymentFeignService {
//上面绑定服务名后, 通过调用接口/payment/get/{id},访问里面具体的服务
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id);
}
6、编写controller
package com.cheng.springcloud.controller;
import com.cheng.springcloud.pojo.CommonResult;
import com.cheng.springcloud.pojo.Payment;
import com.cheng.springcloud.service.PaymentFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderFeignController {
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping(value = "/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
//调用该接口后,会通过定义好的feign功能接口PaymentFeignService,去访问服务提供者里的服务,
// 不需要服务消费者自己去访问服务提供者
return paymentFeignService.getPaymentById(id);
}
}
7、测试
- 启动cloud-eureka-server7001和cloud-eureka-server7002模块
- 启动cloud-provider-payment8001和cloud-provider-payment8002模块
- 启动cloud-consumer-feign-order80模块
访问请求:http://localhost/consumer/payment/get/1
第一次访问:
第二次访问:
继续访问,8002和8001轮流使用,Feign默认实现了轮询的负载均衡算法。
因为Spring Cloud Feign是基于Netflix feign实现,整合了Spring Cloud Ribbon。
3、OpenFeign的超时控制
故意设置超时,演示出错情况
1、在cloud-provider-payment8001服务提供者模块的controller中设置超时方法:
@GetMapping(value = "/payment/feign/timeout")
public String PaymentFeignTimeout(){
try {
//TimeUnit是java.util.concurrent包下面的一个类,TimeUnit提供了可读性更好的线程暂停操作。 SECOND秒
TimeUnit.SECONDS.sleep(3);//线程暂停3秒
} catch (InterruptedException e) {
e.printStackTrace();
}
return serverPort;
}
2、在cloud-consumer-feign-order80模块的feign接口中添加超时方法:
@GetMapping(value = "/payment/feign/timeout")
public String PaymentFeignTimeout();
3、在cloud-consumer-feign-order80模块的controller中访问超时方法:
@GetMapping(value = "/consumer/payment/feign/timeout")
public String PaymentFeignTimeout(){
return paymentFeignService.PaymentFeignTimeout();
}
4、测试
- 启动cloud-eureka-server7001和cloud-eureka-server7002模块
- 启动cloud-provider-payment8001模块
- 启动cloud-consumer-feign-order80模块
自测:访问请求:http://localhost:8001/payment/feign/timeout 延迟三秒后,访问成功,自测没问题!
再用openfeign调用服务接口进行测试:
访问请求:http://localhost/consumer/payment/feign/timeout
报错:
默认Feign客户端只等待一秒钟,但是服务端处理需要超过1秒钟,导致Feign客户端不再等待,直接返回报错。
为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。
cloud-consumer-feign-order80模块yml配置文件添加:
#设置feign客户端超时时间
ribbon:
#建立连接所用的时间
ReadTimeout: 5000
#建立连接后从服务器读取到可用资源的时间
ConncetTimeout: 5000
重启cloud-consumer-feign-order80模块测试:访问请求:http://localhost/consumer/payment/feign/timeout
延迟三秒后成功访问到服务。
4、OpenFeign日志打印功能
有时候我们遇到 Bug,比如接口调用失败、参数没收到等问题,或者想看看调用性能,就需要配置 Feign 的日志打印功能,以此让 Feign 把请求信息输出来。
到日志等级有 4 种,分别是:
- NONE:不输出日志。
- BASIC:只输出请求方法的 URL 和响应的状态码以及接口执行的时间。
- HEADERS:将 BASIC 信息和请求头信息输出。
- FULL:输出完整的请求信息
首先定义一个配置类,代码如下所示。
package com.cheng.springcloud.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
然后在yml文件里配置feign的日志功能:
logging:
level:
# feign日志以什么级别监控哪个接口
com.cheng.springcloud.service.PaymentFeignService: debug
重启cloud-consumer-feign-order80模块测试:访问请求:http://localhost/consumer/payment/get/1,查看后台日志输出: