spring cloud的Hoxton.SR1版本的feign的优雅降级的实现
源码下载
大家可以直接微信扫描上面的二维码关注我的公众号,然后回复hoxton feign里面就会给到源代码的下载地址同时会附上相应的视频教程,并定期的与大家分享相关的技术文章。
前言
在我们基于spring cloud进行开发的时候我们的微服务之间的调用,会由于网络原因、程序原因、数据库等原因导致我们的微服务的调用失败,而失败则会导致抛出错误,这些错误就会不断往上抛,而给到用户很不好的体验,最简单粗暴的处理的方式就是调用其他微服务的时候我们直接try…catch起来直接进行处理,这种处理方式简单粗暴而且后期维护起来也是一个麻烦,因此我们需要一种更加优雅的处理方式,而feign刚好就为我们提供了这种优雅的处理方式,接下来我将为大家讲解如何优雅的实现我们的降级。
feign的优雅降级的实现
首先我们使用intellij开发工具创建一个空的项目,主要用于存放我们的整个工程,整个工程由以下三个项目组成,分别是:feign-demote-eureka【注册中心】、feign-demote-order【订单微服务】、feign-demote-account【账户微服务】。
注册中心
接着我们创建我们的注册中心工程feign-demote-eureka,创建完成以后修改我们的pom.xml修改完以后代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cloud.feign.demote</groupId>
<artifactId>feign-demote-eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>feign-demote-eureka</name>
<description>注册中心</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
接着修改我们的主入口类【FeignDemoteEurekaApplication.java】,将当前的工程配置为eureka的服务端,代码如下:
/**
* @author linzf
* @since 2019-12-26
* 类描述: 注册中心的启动类
*/
@SpringBootApplication
@EnableEurekaServer
public class FeignDemoteEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(FeignDemoteEurekaApplication.class, args);
}
}
最后修改我们的application.yml配置文件,可能你创建的时候是application.properties,直接修改后缀为yml即可,修改以后代码如下:
server:
port: 8761
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
这样我们就完成了我们注册中心的配置了,我们这时候可以直接启动我们的注册中心,然后访问:http://127.0.0.1:8761/就可以看到eureka的页面。
账户微服务
接着我们创建我们的账户微服务【feign-demote-account】,账户微服务没有任何特别的东西,只是提供了两个接口以及将自己注册到注册中心而已,因此我们只需要引入eureka的依赖即可,无需引入我们的feign的依赖,创建完成以后修改我们的pom.xml修改完以后代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cloud.feign.demote</groupId>
<artifactId>feign-demote-account</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>feign-demote-account</name>
<description>账户模块</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<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-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
接着修改我们的主入口类【FeignDemoteAccountApplication.java】,将当前的工程配置为eureka的客户端,代码如下:
/**
* @author linzf
* @since 2019-12-26
* 类描述: 账户的主入口类
*/
@SpringBootApplication
@EnableDiscoveryClient
public class FeignDemoteAccountApplication {
public static void main(String[] args) {
SpringApplication.run(FeignDemoteAccountApplication.class, args);
}
}
最后修改我们的application.yml配置文件,可能你创建的时候是application.properties,直接修改后缀为yml即可,修改以后代码如下:
eureka:
instance:
hostname: localhost
prefer-ip-address: true
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8761/eureka/
# 若将这个参数设置为false,将不会触发feign的降级处理
feign:
hystrix:
enabled: true
logging:
level:
root: INFO
com:
cloud:
feign:
demote:
account: DEBUG
server:
port: 8280
spring:
application:
name: account-server
ribbon:
eureka:
enabled: true
ReadTimeout: 120000
ConnectTimeout: 120000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 0
OkToRetryOnAllOperations: false
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
# 设置断路器超时时间
hystrix:
threadpool:
default:
coreSize: 1000
maxQueueSize: 1000
queueSizeRejectionThreshold: 500
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 490000
strategy: SEMAPHORE
编写我们的【AccountService】的实现,一个方法是正常的方法【feignDemoteCheckSuccess】,另外一个方法是抛出异常的方法【feignDemoteCheckFail】,代码如下:
/**
* @author linzf
* @since 2019/12/26
* 类描述:
*/
@Service
public class AccountService {
private static Logger logger = LoggerFactory.getLogger(AccountService.class);
/**
* 功能描述: 正常的feign的调用
* @return 返回处理结果
*/
public String feignDemoteCheckSuccess(){
logger.debug("我是正常的feign的调用!");
return "success";
}
/**
* 功能描述: 正常的feign的调用
* @return 返回处理结果
*/
public String feignDemoteCheckFail(){
logger.debug("我调用的时候出错了!");
throw new RuntimeException("我被抛出了异常了!");
}
}
最后直接编写我们的接口【AccountController.java】的实现,单纯的接口的编写就不再累述了,直接贴代码:
/**
* @author linzf
* @since 2019/12/26
* 类描述: 账户的controller
*/
@RestController
@RequestMapping(value = "account")
public class AccountController {
@Autowired
private AccountService accountService;
/**
* 功能描述: 正常的feign的调用
* @return 返回处理结果
*/
@GetMapping("feignDemoteCheckSuccess")
public String feignDemoteCheckSuccess(){
return accountService.feignDemoteCheckSuccess();
}
/**
* 功能描述: 正常的feign的调用
* @return 返回处理结果
*/
@GetMapping("feignDemoteCheckFail")
public String feignDemoteCheckFail(){
return accountService.feignDemoteCheckFail();
}
}
到此处我们就完成了我们account-server的编写了,接着我们直接启动我们的account-server。
订单微服务
接着我们创建我们的订单微服务【feign-demote-order】,账户微服务需要调用我们的账户微服务因此需要在引入eureka的基础上海需要引入我们的feign的依赖,创建完成以后修改我们的pom.xml修改完以后代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cloud.feign.demote</groupId>
<artifactId>feign-demote-order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>feign-demote-order</name>
<description>订单模块</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
接着修改我们的主入口类【FeignDemoteOrderApplication.java】,将当前的工程配置为eureka的客户端,同时开启我们的feign,代码如下:
/**
* @author linzf
* @since 2019-12-26
* 类描述: 订单的主入口类
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class FeignDemoteOrderApplication {
public static void main(String[] args) {
SpringApplication.run(FeignDemoteOrderApplication.class, args);
}
}
最后修改我们的application.yml配置文件,可能你创建的时候是application.properties,直接修改后缀为yml即可,修改以后代码如下:
eureka:
instance:
hostname: localhost
prefer-ip-address: true
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8761/eureka/
# 若将这个参数设置为false,将不会触发feign的降级处理
feign:
hystrix:
enabled: true
logging:
level:
root: INFO
com:
cloud:
feign:
demote:
order: DEBUG
server:
port: 8380
spring:
application:
name: order-server
ribbon:
eureka:
enabled: true
ReadTimeout: 120000
ConnectTimeout: 120000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 0
OkToRetryOnAllOperations: false
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
# 设置断路器超时时间
hystrix:
threadpool:
default:
coreSize: 1000
maxQueueSize: 1000
queueSizeRejectionThreshold: 500
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 490000
strategy: SEMAPHORE
编写我们的账户的feign【AccountFeignDemote】用于当前订单服务来调用,代码如下:
package com.cloud.feign.demote.order.feign;
import com.cloud.feign.demote.order.feign.impl.AccountFeignDemoteImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author linzf
* @since 2019/12/26
* 类描述:
*/
@FeignClient(value = "account-server", path = "account" ,fallback = AccountFeignDemoteImpl.class)
public interface AccountFeignDemote {
/**
* 功能描述: 正常的feign的调用
*
* @return 返回处理结果
*/
@GetMapping("feignDemoteCheckSuccess")
String feignDemoteCheckSuccess();
/**
* 功能描述: 正常的feign的调用
*
* @return 返回处理结果
*/
@GetMapping("feignDemoteCheckFail")
String feignDemoteCheckFail();
}
编写我们的账户降级的feign【AccountFeignDemote】的实现,用于当调用失败的时候的降级处理,代码如下:
package com.cloud.feign.demote.order.feign.impl;
import com.cloud.feign.demote.order.feign.AccountFeignDemote;
import org.springframework.stereotype.Component;
/**
* @author linzf
* @since 2019/12/26
* 类描述:
*/
@Component
public class AccountFeignDemoteImpl implements AccountFeignDemote {
@Override
public String feignDemoteCheckSuccess() {
return "我被降级处理了";
}
@Override
public String feignDemoteCheckFail() {
return "我被降级处理了";
}
}
编写订单的service【OrderService.java】的实现,代码如下:
package com.cloud.feign.demote.order.service;
import com.cloud.feign.demote.order.feign.AccountFeignDemote;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author linzf
* @since 2019/12/26
* 类描述:
*/
@Service
public class OrderService {
private static Logger logger = LoggerFactory.getLogger(OrderService.class);
@Autowired
private AccountFeignDemote accountFeignDemote;
/**
* 功能描述: 有降级的feign调用的成功的方法
* @return
*/
public String feignDemoteCheckSuccess(){
logger.debug("这是有降级的调用成功以后的方法!");
return accountFeignDemote.feignDemoteCheckSuccess();
}
/**
* 功能描述: 有降级的feign调用的失败的方法
* @return
*/
public String feignDemoteCheckFail(){
logger.debug("这是有降级的调用失败以后的方法!");
return accountFeignDemote.feignDemoteCheckFail();
}
}
编写订单的controller【OrderController.java】的实现,代码如下:
package com.cloud.feign.demote.order.controller;
import com.cloud.feign.demote.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author linzf
* @since 2019/12/26
* 类描述: 订单的controller
*/
@RestController
@RequestMapping(value = "order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 功能描述: 有降级的feign调用的成功的方法
* @return
*/
@GetMapping("feignDemoteCheckSuccess")
public String feignDemoteCheckSuccess(){
return orderService.feignDemoteCheckSuccess();
}
/**
* 功能描述: 有降级的feign调用的失败的方法
* @return
*/
@GetMapping("feignDemoteCheckFail")
public String feignDemoteCheckFail(){
return orderService.feignDemoteCheckFail();
}
}
到此处我们就完成了我们的订单微服务开发了,我们直接启动我们的订单微服务。
验证feign的降级
在保证我们的三个服务都正常启动,且我们的订单和账户都注册到注册中心的情况下,我们直接打开浏览器输入以下的地址:
http://127.0.0.1:8380/order/feignDemoteCheckFail,我们在控制台会看到如下的页面,则说明服务被降级了:
若我们希望不要进行全局的降级处理,那我们就直接修改我们的application.yml的配置文件的以下的代码部分,将改值由true改为false即可。
若我们只希望我们的某个feign不需要降级处理,那么我们直接删除相应的feign接口的fallback即可,删除如下图箭头所示位置的代码即可:
到此处我们就完成了我们的feign的优雅的降级的demo的实现了,若需要源代码可以关注我的公众号回复hoxton feign即可