熔断降级解决方案之Hystrix

1,017

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

目前主流的服务熔断降级方案,主要有两种:springcloud的Hystrix和Alibaba的Sentinel,不论是从整体热度及使用体验、功能额丰富而言,Sentinel都具有绝对的优势。

为什么需要服务的熔断呢?

当前的微服务架构,拥有多个服务,相互间的调用复杂,当有一台服务出现问题,导致服务无法调用,此时流量上来后,会造成整个系统的流量堆积,最终造成整个系统的不可用。所用服务的熔断是很重要的,可以维护系统的问题及可用性。

熔断主要注意两点:启动熔断和恢复熔断。

什么是服务降级呢?

当系统在某个时间段迎来了流量洪峰,整个系统的压力倍增,此时为了保证核心业务的运行,可以对某些无关紧要的服务和页面进行流量限制或者停止访问,从而释放服务器的整体压力。

下面我们分别演示使用Hystrix。

Hystrix

代码编写

引入依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>

加入开启熔断器的注解@EnableCircuitBreaker,我此处演示项目使用gateway演示:

/**
 * 启动类
 *
 * @author weirx
 */
@EnableCircuitBreaker
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}, scanBasePackages = {"com.cloud.bssp.gateway"})
@EnableFeignClients(basePackages = {"com.cloud.bssp.gateway.feign"})
public class BsspGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(BsspGatewayApplication.class, args);
    }

}

默认情况下FeignClient中默认情况下是禁用Hystrix的,所以如果需要在微服务中启用Hystrix的熔断功能,则需要通过配置手动开启Hystrix功能:

#开启hystrix
feign:
  hystrix:
    enabled: true

提供一个用户服务的feignClient,重点是属性fallbackFactory,后面配置当前feignClient的实现类。

@FeignClient(name = "bssp-user-service", path = "/user",fallbackFactory = UserClientImpl.class)
public interface UserClient {

    @PostMapping("/login")
    R login(@RequestBody UserDTO userDTO);
}

UserClientImpl代码如,注意增加@Component注解,否则可能会报找不到服务的错误:

/**
 * @author weirx
 * @date 2021/07/08 11:10
 **/
@Slf4j
@Component
public class UserClientImpl implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
        return new UserClient() {
            @Override
            public R login(UserDTO userDTO) {
                log.info("调用用户服务失败,对用户服务降级处理。");
                return R.failed("调用用户服务失败,对用户服务降级处理");
            }
        };
    }
}

模拟user服务宕机情况下,访问登录接口,之所以用登录接口是图方便。将user服务关闭,或者在注册中心下线处理。查看结果:

image.png

Histrix提供了面板共我们直观的查看:HystrixDashboard,引入依赖,需要注意的是,尽量不要在springcloudGateway中添加dashboard,因为其依赖含有web组件,会和gateway冲突,所以下面的演示我换一个服务,这是自己踩得一个坑:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>

启动类增加注解@EnableHystrixDashboard

/**
 * @author weirx
 * @description: 数据服务启动类
 * <p>
 * 注解@ServletComponentScan: 使Servlet,filter,listener 可以通过 @WebServlet,@WebFilter,@WebListener
 * 等注解直接注册,无需其他代码
 * @date 2020/6/17
 */
@EnableHystrixDashboard
@EnableCircuitBreaker
@SpringBootApplication
@ServletComponentScan
@EnableFeignClients
public class BsspAdminServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(BsspAdminServiceApplication.class, args);
    }
}

结果展示

访问http://localhost:8080/hystrix

image.png

下面是我的client和fallback实现:

/**
 * Description: 用户表
 * Create Date: 2021-03-17T13:53:59.025
 * Modified By:<br>
 * Modified Date:<br>
 * Why & What is modified:<br>
 *
 * @author weirx
 * @version 1.0
 */
@FeignClient(name = "bssp-user-service", path = "/user", fallbackFactory = UserClientImpl.class)
public interface UserClient {

    /**
     * 分页列表
     *
     * @param params
     * @return
     */
    @PostMapping("/pageList")
    R pageList(@RequestBody Map<String, Object> params);

    /**
     * list列表
     *
     * @param userDTO
     * @return
     */
    @PostMapping("/list")
    R list(@RequestBody UserDTO userDTO);

    /**
     * 根据主键查询
     *
     * @param id
     * @return
     */
    @GetMapping("/info/getById")
    R info(@RequestParam("id") Long id);

    /**
     * 新增
     *
     * @param userDTO
     * @return
     */
    @PostMapping("/save")
    R save(@RequestBody UserDTO userDTO);

    /**
     * 更新
     *
     * @param userDTO
     * @return
     */
    @PostMapping("/update")
    R update(@RequestBody UserDTO userDTO);
}
import java.util.Map;

/**
 * @author weirx
 * @date 2021/07/08 14:38
 **/
@Component
public class UserClientImpl implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
        return new UserClient() {
            @Override
            public R pageList(Map<String, Object> params) {
                return R.failed("调用用户服务失败,服务已被降级处理");
            }

            @Override
            public R list(UserDTO userDTO) {
                return R.failed("调用用户服务失败,服务已被降级处理");
            }

            @Override
            public R info(Long id) {
                return R.failed("调用用户服务失败,服务已被降级处理");
            }

            @Override
            public R save(UserDTO userDTO) {
                return R.failed("调用用户服务失败,服务已被降级处理");
            }

            @Override
            public R update(UserDTO userDTO) {
                return R.failed("调用用户服务失败,服务已被降级处理");
            }
        };
    }
}

测试controller:

@Api(tags = "测试Hystrix")
@RestController
@RequestMapping("/test")
public class TestController {

    /**
     * SysMenuService
     */
    @Autowired
    private UserClient userClient;


    /**
     * list列表
     * @return
     */
    @GetMapping("/list")
    public R list() {
        return userClient.list(new UserDTO());
    }

}

颜色说明:

image.png

现在通过dashboard监控服务,当用户服务正常启动是,我们多次访问端口看结果:

image.png

当我们将服务关闭后访问看结果:

image.png