dubbo集成netflix原生的hystrix框架

一、需求描述

需要将一个现有项目添加熔断功能,而这个项目的技术栈为spring、dubbo等,并且使用的dubbo-Main方式启动。

二、存在问题

由于项目并未使用web容器、也未使用spring-boot。而目前能拿到的资料中,都是在spring-boot下进行hystrix集成的,不得不说spring-boot的开箱即用理念,集成起来真的非常方便。因此,基于当前结构无法快速集成,也就需要开始排坑。

三、解决思路

目前有几个方向来解决问题:
1、当现实情况改造为spring-boot不是上策
2、改造为web容器启动会引入tomcat等组件,运维不一定答应
3、想办法保持dubbo的Main方式启动、spring保留,找办法。

四、开始排坑

前提条件

1、对Hystrix框架要熟悉使用
2、对dubbo框架有一定认知,熟悉Filter使用

四-1、dubbo服务集成Hystrix

找思路

根据3中的方向,需要准备:
1、查看spring-cloud-starter-hystrix中的依赖,决定使用netflix的原生hystrix组件,spring-cloud能打包,我也可以。
2、引入了hystrix-core和hystrix-javanica

测试思路(Hystrix是否生效)

1、注解方式配置@HystrixCommand等,测试接口,发现异常情况下没有进入降级流程。
2、应该是dubbo框架对内部异常进行了封装,导致异常抛出后直接进入了dubbo处理流程
3、定义一个Filter,拦截dubbo的请求,对response进行分析,拿到success/exception,根据这里的结构,进入降级流程。
4、根据3的思路,需要实现HystrixCommand接口,进而在Filter中进行集成。

定义Filter

import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;

@Activate(group = "consumer")
public class HystrixFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        DubboHystrixCommand command = new DubboHystrixCommand(invoker, invocation, "aaa");
        Result res = command.execute();
        return res;
    }
}

实现HystrixCommand

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcResult;
import com.netflix.hystrix.*;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;


public class DubboHystrixCommand extends HystrixCommand<Result> {

    private static Logger logger = LoggerFactory.getLogger(DubboHystrixCommand.class);
    private static final int DEFAULT_THREADPOOL_CORE_SIZE = 30;
    private Invoker invoker;
    private Invocation invocation;
    private String fallbackName;

    public DubboHystrixCommand(Invoker<?> invoker, Invocation invocation, String fallbackName) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(invoker.getInterface().getName()))
                .andCommandKey(HystrixCommandKey.Factory.asKey(String.format("%s_%d", invocation.getMethodName(), invocation.getArguments() == null ? 0 : invocation.getArguments().length)))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withCircuitBreakerRequestVolumeThreshold(20)//10秒钟内至少19此请求失败,熔断器才发挥起作用
                        .withCircuitBreakerSleepWindowInMilliseconds(3000)//熔断器中断请求30秒后会进入半打开状态,放部分流量过去重试
                        .withCircuitBreakerErrorThresholdPercentage(50)//错误率达到50开启熔断保护
                        .withExecutionTimeoutEnabled(false)//使用dubbo的超时,禁用这里的超时
                        )
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
                        .withCoreSize(getThreadPoolCoreSize(invoker.getUrl()))));//线程池为30

        this.invoker = invoker;
        this.invocation = invocation;
        this.fallbackName = fallbackName;
    }


    /**
     * 获取线程池大小
     * <dubbo:parameter key="ThreadPoolCoreSize" value="20" />
     *
     * @param url
     * @return
     */
    private static int getThreadPoolCoreSize(URL url) {
        if (url != null) {
            int size = url.getParameter("ThreadPoolCoreSize", DEFAULT_THREADPOOL_CORE_SIZE);
            if (logger.isDebugEnabled()) {
                logger.debug("ThreadPoolCoreSize:" + size);
            }
            return size;
        }

        return DEFAULT_THREADPOOL_CORE_SIZE;

    }

    @Override
    protected Result run() throws Exception {
        Result res = invoker.invoke(invocation);
        if (res.hasException()) {
            throw new HystrixRuntimeException(HystrixRuntimeException.FailureType.COMMAND_EXCEPTION,
                    DubboHystrixCommand.class,
                    res.getException().getMessage(),
                    res.getException(), null);
        }
        return res;
    }

    @Override
    protected Result getFallback() {
        if (StringUtils.isEmpty(fallbackName)) {
            //抛出原本的异常
            return super.getFallback();
        }
        return new RpcResult("the dubbo fallback.");
    }
}

注册、使用Filter

hystrixFilter=*****.HystrixFilter

测试结果:可以正常进入降级流程,并且实现熔断功能(熔断、半开、恢复)

<dubbo:service interface="*****.service.HelloService" ref="helloService"  timeout="*****" filter="hystrixFilter">
    <dubbo:method name="say" retries="0" />
    <dubbo:parameter key="ThreadPoolCoreSize" value="30" />
</dubbo:service>

四-2、集成Hystrix metric stream

找思路

1、查看spring-cloud-starter-hystrix-dashboard中的依赖,引入了hystrix-metrics-event-stream和hystrix-dashboard
2、引入hystrix-dashboard后发现maven有异常,翻看资料发现hystrix-dashboard只是一个web项目,需要独立部署(这一问题是到最后才发现并解决的),因此删除hystrix-dashboard的依赖。
3、hystrix-metrics-event-stream的作用,是收集hystrix统计的接口信息,比如success、exception次数等,并输出,http://xxxx/hystrix.stream 返回信息就是metric结果。

测试思路 (能否输出metric)

1、扩展JettyContainer,找到dubbo自己的jettyContainer实现,复制出来,对start()方法进行改动:

public void start() {
	String serverPort = ConfigUtils.getProperty("dubbo.jetty.port");
	int port;
	if (serverPort != null && serverPort.length() != 0) {
		port = Integer.parseInt(serverPort);
	} else {
		port = 8080;
	}

	this.connector = new SelectChannelConnector();
	this.connector.setPort(port);
	ServletHandler handler = new ServletHandler();
	String resources = ConfigUtils.getProperty("dubbo.jetty.directory");
	if (resources != null && resources.length() > 0) {
		FilterHolder pageHolder = handler.addFilterWithMapping(ResourceFilter.class, "/*", 0);
		pageHolder.setInitParameter("resources", resources);
	}

	ServletHolder pageHolder1 = handler.addServletWithMapping(PageServlet.class, "/*");
	pageHolder1.setInitParameter("pages", ConfigUtils.getProperty("dubbo.jetty.page"));
	pageHolder1.setInitOrder(2);

	// 熔断监控,这里就是增加servlet-mapping
	handler.addServletWithMapping(HystrixMetricsStreamServlet.class, "/hystrix.stream");

	Server server = new Server();
	server.addConnector(this.connector);
	server.addHandler(handler);

	try {
		server.start();
	} catch (Exception var8) {
		throw new IllegalStateException("Failed to start jetty server on " + NetUtils.getLocalHost() + ":" + port + ", cause: " + var8.getMessage(), var8);
	}
}

2、将扩展的JettyContainer进行注册,也就是在META-INF/目录下进行配置:
创建文件META-INF/dubbo/com.alibaba.dubbo.container.Container,内容如下

myjetty=com.gomefinance.hystrix.spring.container.JettyContainer

3、在dubbo.properties文件中,增加容器启动信息

dubbo.container=spring,myjetty
# 访问端口
dubbo.jetty.port=8088

4、启动项目,访问http://localhost:8088/hystrix.stream

如果浏览器显示大量ping..说明成功,此时请求集成了熔断组件的接口,该页面会显示metric信息。

四-3、集成Hystrix dashboard

找思路

1、目前是有了metric信息,需要图形化展示才行。
2、从github上将hystrix dashboard的源码clone下来,打包为war进行部署。
3、目前hystrix主项目的master分支中,没有找到dashboard目录,因此找到另外一个
https://github.com/Netflix-Skunkworks/hystrix-dashboard
经过验证可以使用,我没有细研究这个仓库与Netflix的关系。

测试思路 (能否展示dashboard)

1、将github上的代码下载到本地,然后通过IDEA打开,由于是gradle项目,期间会加载依赖比较缓慢,加载完成后,通过右侧Gradle projects->Execute Gradle Task,输入appRun/jettyRun/build命令中的任意(此处是找资料过程中各种尝试),不过感觉只执行build命令即可。
2、提示build success后,在build/libs/目录下,找到war包,这个war包带版本号,如果不想带,自己找资料学一下配置方式。
3、将war放到tomcat的webapps目录下,启动tomcat,访问http://localhost:8080/hystrix 我这里是修改了解压后的目录名。
4、如果显示Hystrix页面,则说明部署成功

五、组装飙车

目前已有:
1、集成了Hystrix框架的dubbo服务
2、能拿到Hystrix-metric-stream数据的URL:http://localhost:8088/hystrix.stream
3、部署好的Hystrix-dashboard:http://localhost:8080/hystrix
4、写一个单元测试类

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/META-INF/dubbo/dubbo-reference.xml"})
public class HelloServiceTest {

    @Resource
    private HelloService helloService;

    @Test
    public void say() throws InterruptedException {
        for (int i = 0; i < 1; i++) {
            String res = helloService.say();
            System.out.println("test" + i + ":" + res + " The time:" + (System.currentTimeMillis() / 1000));
            Thread.sleep(200);
        }
    }
}

组装

1、启动dubbo服务、启动dashboard
2、将http://localhost:8088/hystrix.stream配置到dashboard,点击【add stream】,再点击【Monitor Streams】,进入监控页面
3、执行单元测试,观察监控页面状态,如果有曲线变化、数据变化,ok搞定。


资料看了很多,也没有记下来,简单感谢一下bing.com and baidu.com

当然如果对您有帮助,您也可以更加直白的感谢一下;您的支持是我最大的动力。 

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值