[学习微服务] ServiceComb + Spring Cloud Hystrix

点击蓝色文字添加关注!推荐指数★★★★★


在分布式环境中,许多服务依赖项中的一些不可避免地会失败。Hystrix是一个Java类库,可通过添加容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点,阻止它们之间的级联故障以及提供后备选项来实现这一目标,所有这些都可以提高系统的整体弹性。

ServiceComb内置了组件handler-bizkeeper实现和服务降级相关的功能,比如隔离、熔断、容错。开发者可以非常简单的使用。handler-bizkeeper组件基于Netflix Hystrix实现。具体可参考官方网址↓↓↓

https://docs.servicecomb.io/java-chassis/zh_CN/build-provider/configuration/downgrade-strategy.html


SpringCloud也有基于Netflix Hystrix实现的一套服务降级工具Spring Cloud Hystrix。

开发者在ServiceComb框架中可以自由选择使用其中一个库。

640?wx_fmt=gif 下面通过一个场景演示ServiceComb结合Spring Cloud Hystrix的使用


使用步骤
  1. 排除ServiceComb的内置组件handler-bizkeeper(注意依赖传递)

  2. 添加依赖org.springframework.cloud:spring-cloud-starter-hystrix

完成以上步骤后即可使用spring-cloud-starter-hystrix。(注意spring-cloud-starter-hystrix本身生效应加的配置,如EnableCircuitBreaker注解)示例

本篇以典型的服务间调用超时,和数据库服务调用超时来演示服务降级的使用。如下图所示,在服务Service_A当中使用Hystrix分别封装了数据库访问和服务间的调用。为方便起见,使用内存数据库H2 (如下图)

640?wx_fmt=png

完整示例地址:https://github.com/lisenwork/servicecomb-demo/blob/master/servicecomb-hystrix

一. 启动ServiceCenter注册中心

开发者可参考官方README https://github.com/apache/servicecomb-service-center/blob/master/README.md 进行下载安装和启动

二. 服务消费者Service_A
1. 添加依赖

新建pom.xml文件,加入以下关键依赖

<dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-hystrix</artifactId>    <version>1.4.4.RELEASE</version></dependency><dependency>    <groupId>org.apache.servicecomb</groupId>    <artifactId>spring-boot-starter-provider</artifactId>    <version>1.1.0</version>    <exclusions>        <exclusion>            <groupId>org.apache.servicecomb</groupId>            <artifactId>handler-bizkeeper</artifactId>        </exclusion>    </exclusions></dependency><!-- 内存数据库 --><dependency>    <groupId>com.h2database</groupId>    <artifactId>h2</artifactId></dependency><!-- 数据库ORM框架 --><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-jpa</artifactId></dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.4.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.apache.servicecomb</groupId>
    <artifactId>spring-boot-starter-provider</artifactId>
    <version>1.1.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.servicecomb</groupId>
            <artifactId>handler-bizkeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 内存数据库 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

<!-- 数据库ORM框架 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

完整pom.xml文件请见 

https://github.com/lisenwork/servicecomb-demo/blob/master/servicecomb-hystrix/Service_A/pom.xml

2. 配置

(1) 在resources目录下新建ServiceComb配置文件microservice.yaml,添加微服务定义信息

 
 
APPLICATION_ID: service-hystrix  # 应用名,注意与网关服务器的应用名保持一致service_description:  name: service_a  # 微服务名  version: 0.0.4     # 微服务版本号servicecomb:  service:    registry:      address: http://127.0.0.1:30100  # 服务注册中心(重要)  rest:      address: 0.0.0.0:8989   # 微服务启动端口


(2) 在resources目录下新建ServiceComb配置文件application.yml,添加数据库配置信息

 
 
spring:  datasource:    url: jdbc:h2:mem:h2test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE    platform: h2    username: sa    password:    driverClassName: org.h2.Driver    schema: classpath:db/schema.sql    data: classpath:db/data.sql  jpa:    database-platform: org.hibernate.dialect.H2Dialect    hibernate:      ddl-auto: update    properties:      hibernate:        show_sql: true        use_sql_comments: true        format_sql: truelogging:  level: debug


(3) 添加数据库初始化脚本。在resources/db目录下新建data.sql和schema.sql。
schema.sql

 
 
CREATE TABLE t_user( id INTEGER NOT NULL,name VARCHAR(25) NOT NULL, age INTEGER DEFAULT NULL)TABLE t_user( id INTEGER NOT NULL,name VARCHAR(25NOT NULL, age INTEGER DEFAULT NULL)

data.sql

 
 
INSERT INTO t_user(id,name,age)VALUES ('1','张三',18);INSERT INTO t_user(id,name,age)VALUES ('2','李四',19);INTO t_user(id,name,age)VALUES ('1','张三',18);
INSERT INTO t_user(id,name,age)VALUES ('2','李四',19);


3.项目入口

新建启动类Application。其中EnableCircuitBreaker是为了使Spring-Cloud-Hystrix生效。EnableServiceComb是使ServiceComb生效。RestTemplate对象用于后面的服务间接口调用↓↓↓

 
 
@SpringBootApplication@EnableServiceComb@EnableCircuitBreakerpublic class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }    @LoadBalanced    @Bean    public RestTemplate getRestTemplate(){        return new RestTemplate();    }}
@EnableServiceComb
@EnableCircuitBreaker
public class Application {

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

    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}


4. 模拟业务代码

新建AController类提供对外的接口↓↓↓

 
 
@RestSchema(schemaId = "aController")@RequestMapping("/")public class AController{    @Autowired    private  AService aService;    @GetMapping("/getSomethingFromServiceB")    public String getSomethingFromServiceB(){        return aService.getSomethingFromServiceB();    }    @GetMapping("/getSomethingFromDataSource")    public List<User> getSomethingFromDataSource(){        return aService.getSomethingFromDataSource();    }}"aController")
@RequestMapping("/")
public class AController
{

    @Autowired
    private  AService aService;

    @GetMapping("/getSomethingFromServiceB")
    public String getSomethingFromServiceB(){
        return aService.getSomethingFromServiceB();
    }

    @GetMapping("/getSomethingFromDataSource")
    public List<User> getSomethingFromDataSource(){
        return aService.getSomethingFromDataSource();
    }

}


新建AService类模拟业务逻辑

 
 
@Servicepublic class AService {    @Autowired    LoadBalancerClient loadBalancerClient;    @Autowired    RestTemplate restTemplate;    @Autowired    private UserRepository userRepository;    @HystrixCommand(            fallbackMethod = "errorHandler",            commandProperties = {                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")            }    )    public String getSomethingFromServiceB() {        ServiceInstance serviceInstance = loadBalancerClient.choose("service_b");        String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort()+"/getSomething";        System.out.println(url);        return restTemplate.getForObject(url, String.class);    }    @HystrixCommand(fallbackMethod = "defaultStores",            commandProperties = {                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")            }    )    public List<User> getSomethingFromDataSource() {        randomlyRunLong();        List<User> userList = userRepository.findAll();        userList.forEach(System.out::println);        return userList;    }    public List<User> defaultStores() {        System.out.println("defaultStores");        return new ArrayList<>();    }    public String errorHandler(Throwable throwable){        return throwable.toString();    }    private void randomlyRunLong(){        Random rand = new Random();        int randomNum = rand.nextInt((3 - 1) + 1) + 1;        if (randomNum==3) sleep();    }    private void sleep(){        try {            Thread.sleep(20000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}
public class AService {

    @Autowired
    LoadBalancerClient loadBalancerClient;
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    private UserRepository userRepository;

    @HystrixCommand(
            fallbackMethod = "errorHandler",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")
            }
    )
    public String getSomethingFromServiceB() {
        ServiceInstance serviceInstance = loadBalancerClient.choose("service_b");
        String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort()+"/getSomething";
        System.out.println(url);
        return restTemplate.getForObject(url, String.class);
    }

    @HystrixCommand(fallbackMethod = "defaultStores",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
            }
    )
    public List<User> getSomethingFromDataSource() {

        randomlyRunLong();
        List<User> userList = userRepository.findAll();
        userList.forEach(System.out::println);
        return userList;
    }

    public List<User> defaultStores() {
        System.out.println("defaultStores");
        return new ArrayList<>();
    }

    public String errorHandler(Throwable throwable){
        return throwable.toString();
    }


    private void randomlyRunLong(){
        Random rand = new Random();
        int randomNum = rand.nextInt((3 - 1) + 1) + 1;
        if (randomNum==3) sleep();
    }

    private void sleep(){
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}


三. 服务提供者Service_B

Service_B的代码比较简单,只是对外提供一个接口,并加了随机延时
接口定义如下↓↓↓

 
 
@RestSchema(schemaId = "bController")@RequestMapping("/")public class BController {    @GetMapping("/getSomething")    public String getSomething(){        randomlyRunLong();        return "Hello";    }    private void randomlyRunLong(){        Random rand = new Random();        int randomNum = rand.nextInt((3 - 1) + 1) + 1;        if (randomNum==3) sleep();    }    private void sleep(){        try {            Thread.sleep(20000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}"bController")
@RequestMapping("/")
public class BController {

    @GetMapping("/getSomething")
    public String getSomething(){
        randomlyRunLong();
        return "Hello";
    }

    private void randomlyRunLong(){
        Random rand = new Random();
        int randomNum = rand.nextInt((3 - 1) + 1) + 1;
        if (randomNum==3) sleep();
    }

    private void sleep(){
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


完整Service_B项目代码请参考 https://github.com/lisenwork/servicecomb-demo/blob/master/servicecomb-hystrix/Service_B/pom.xml

四. 演示


> > > > 启动服务Service_A和Service_B

1. 数据库的访问

在浏览器(或Postman)访问http://localhost:8989/getSomethingFromDataSource,多次后会出现返回空数组的现象。


2. 服务间的调用

在浏览器(或Postman)访问http://localhost:8989/getSomethingFromServiceB,多次后会出现返回异常信息的现象



文末小结

本文向社区读者从使用角度阐述了ServiceComb是如何支持Spring Cloud Hystrix的。

我们也非常欢迎爱好者们向社区提问和贡献代码:)

如果在阅读代码时有任何疑问想交流,欢迎扫码加入进微信群。

期待志同道合的朋友们加入

ServiceComb的大门为你们敞开~

扫描二维码,添加微服务小助手

640?wx_fmt=jpeg 用心做开源/不忘初衷 640?wx_fmt=png


640?wx_fmt=gif

前期阅读


[学习微服务]

ServiceComb之Java-Chassis启动流程分析

[学习微服务第10天] 

Service-Center 启动流程分析

[学习微服务第9天] 

Service-Center使用入门

[学习微服务-第8天]

ServiceComb内置负载均衡组件handler-loadbalance

[学习微服务第7天] 

ServiceComb+SpringCloud Ribbon源码解读

[学习微服务-第6天] 

负载均衡之ServiceComb + SpringCloud Ribbon

[学习微服务-第5天]

ServiceComb+Zipkin源码解读

[学习微服务-第4天]

ServiceComb+Zipkin

[学习微服务-第3天] 

ServiceComb内置高性能网关服务

[每天学习微服务-源码解读]

 ServiceComb+SpringCloud Zuul

[每天学习微服务-网关]

ServiceComb+SpringCloud Zuul

640?wx_fmt=png


了解更多信息请访问: 

官方网站 http://servicecomb.apache.org/ 

Github代码仓库 https://github.com/apache?q=ServiceComb 


640?wx_fmt=png好看你就 点点640?wx_fmt=png 640?wx_fmt=gif


赶紧点击阅读原文阅读相关源码

并给ServiceComb点个“Star”吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值