HystrixCommand实战

Posted sky-chen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HystrixCommand实战相关的知识,希望对你有一定的参考价值。

1. HystrixCommand实战

1.1. 需求

  1. 由于前端公共调用入口接口代码,封装在单独的jar包,它不属于springCloud管理,所以不适合用注解的方式@HystrixCommand进行服务降级
  2. 这里直接通过HystrixCommand的原生实现方式,对服务进行服务降级限流

1.2. 代码

package com.zhiyis.common.command;

import com.alibaba.fastjson.JSON;
import com.netflix.hystrix.*;
import com.zhiyis.common.bean.bus.OtherFields;
import com.zhiyis.common.cache.HashMapCache;
import com.zhiyis.common.model.ErrorMsg;
import com.zhiyis.common.report.RequestReport;
import com.zhiyis.common.report.ResponseReport;
import com.zhiyis.common.service.TableService;
import com.zhiyis.common.service.TokenService;
import com.zhiyis.common.utils.ApplicationContextProvider;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 断路器
 *
 * @author laoliangliang
 * @date 2019/1/2 10:24
 */
public class RpcCommand extends HystrixCommand<ResponseReport> {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private TableService tableService;

    private ApplicationContextProvider applicationContextProvider;

    private TokenService tokenService;

    private String report;
    private OtherFields fields;
    private HttpServletRequest request;

    public RpcCommand(TableService tableService,
                      ApplicationContextProvider applicationContextProvider,
                      TokenService tokenService,
                      String report, OtherFields fields, HttpServletRequest request) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("rpcGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("rpcCommand"))
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("rpcThreadPool"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(3000)));
        this.tableService = tableService;
        this.applicationContextProvider = applicationContextProvider;
        this.tokenService = tokenService;
        this.report = report;
        this.fields = fields;
        this.request = request;
    }

    @Override
    protected ResponseReport run() throws Exception {
        logger.info("The report received :" + report);
        RequestReport requestReport = JSON.parseObject(report, RequestReport.class);
        requestReport.setOtherFields(fields);
        String name = Thread.currentThread().getName();
        long start = System.currentTimeMillis();
        String rand = start + String.valueOf((new Random()).nextInt(10));
        logger.info("—————————————" + rand + "启动线程:" + name + "————————————————————");
        ResponseReport responseReport = new ResponseReport();
        logger.info("The requestReport is:" + report);
        logger.debug("The body is:{}", requestReport.getBody());
        logger.debug("The sign is:{}", requestReport.getHeader().getSign());
        String traCode = requestReport.getHeader().getTra_code();
        if (traCode.isEmpty()) {
            responseReport = responseReport.returnError(ErrorMsg.TRADE_CODE_IS_EMPTY, requestReport);
        } else {
            Map<String, Object> rpcMap = HashMapCache.RPC_INFO.get(traCode);
            if (rpcMap != null) {
                //判断是否需要校验Token
                if (rpcMap.get("is_token_check") != null && String.valueOf(rpcMap.get("is_token_check")).equals("1")) {
                    String token = requestReport.getHeader().getToken();
                    if (StringUtils.isEmpty(token)) {
                        responseReport = responseReport.returnError(ErrorMsg.TOKEN_IS_EMPTY, requestReport);
                        return responseReport;
                    } else {
                        switch (tokenService.checkToken(token)) {
                            case 0:
                                responseReport = responseReport.returnError(ErrorMsg.TOKEN_IS_INVALID, requestReport);
                                return responseReport;
                            case 2:
                                responseReport = responseReport.returnError(ErrorMsg.TOKEN_TRA_CODE_NOT_CONIG, requestReport);
                                return responseReport;
                        }
                    }
                }
                String tableName = (String) rpcMap.get("tb_name");
                switch ((int) rpcMap.get("rpc_type")) {
//                    增加单条记录
                    case 1:
                        responseReport = tableService.addRecord(rpcMap, tableName, requestReport);
                        break;
//                    获取单条记录
                    case 2:
                        responseReport = tableService.getRecord(Arrays.asList(((String) rpcMap.get("query_fields")).split(",")), tableName, requestReport);
                        break;
//                    获取多条记录
                    case 3:
                        responseReport = tableService.getRecords(Arrays.asList(((String) rpcMap.get("query_fields")).split(",")), tableName, requestReport);
                        break;
//                    修改记录
                    case 4:
                        responseReport = tableService.updateRecord(tableName, ((String) rpcMap.get("query_fields")).split(","), requestReport);
                        break;
//                    自定义接口
                    case 5:
                        Object clazz = applicationContextProvider.getBean((String) rpcMap.get("class_name"));
                        String methodName = (String) rpcMap.get("class_func_name");
                        Method method = ReflectionUtils.findMethod(clazz.getClass(), methodName, RequestReport.class);
                        responseReport = (ResponseReport) ReflectionUtils.invokeMethod(method, clazz, requestReport);
                        break;
//                    获取单条记录自定义SQL
                    case 6:
                        responseReport = tableService.getSingleRecordBySQL((String) rpcMap.get("sql_text"), requestReport);
                        break;
//                    获取多条记录自定义SQL
                    case 7:
                        responseReport = tableService.getMultipleRecordBySQL((String) rpcMap.get("sql_text"), requestReport);
                        break;
//                    单文件上传的自定义接口
                    case 8:
                        MultipartFile file = null;
                        try {
                            Map<String, MultipartFile> fileMap = ((MultipartHttpServletRequest) request).getFileMap();
                            if (fileMap != null && fileMap.size() != 0) {
                                file = fileMap.values().iterator().next();
                            }
                        } catch (ClassCastException e) {
                            logger.info("未提供图片");
                        }
                        Object clazz2 = applicationContextProvider.getBean((String) rpcMap.get("class_name"));
                        String methodName2 = (String) rpcMap.get("class_func_name");
                        Method method2 = ReflectionUtils.findMethod(clazz2.getClass(), methodName2, RequestReport.class, MultipartFile.class);
                        responseReport = (ResponseReport) ReflectionUtils.invokeMethod(method2, clazz2, requestReport, file);
                        break;
//                    单个或多文件上传的自定义接口
                    case 9:
                        List<MultipartFile> fileList = new LinkedList<>();
                        try {
                            MultiValueMap<String, MultipartFile> multiFileMap = ((MultipartHttpServletRequest) request).getMultiFileMap();
                            for (String key : multiFileMap.keySet()) {
                                for (int i = 0; i < multiFileMap.get(key).size(); i++) {
                                    MultipartFile multipartFile = multiFileMap.get(key).get(i);
                                    fileList.add(multipartFile);
                                }
                            }
                        } catch (ClassCastException e) {
                            logger.info("未提供图片");
                        }
                        Object clazz3 = applicationContextProvider.getBean((String) rpcMap.get("class_name"));
                        String methodName3 = (String) rpcMap.get("class_func_name");
                        Method method3 = ReflectionUtils.findMethod(clazz3.getClass(), methodName3, RequestReport.class, List.class);
                        responseReport = (ResponseReport) ReflectionUtils.invokeMethod(method3, clazz3, requestReport, fileList);
                        break;
                    default:
                        break;
                }
                logger.info("The responseResult is:" + JSON.toJSONString(responseReport));
            }
        }
        long end = System.currentTimeMillis();
        long term = end - start;
        logger.info("—————————————" + rand + "结束线程:" + name + ",耗时:" + term + "ms——————————————");
        return responseReport;
    }

    @Override
    protected ResponseReport getFallback() {
        Throwable e = getExecutionException();
        if (e != null) {
            logger.error("rpc 异常",e);
        }
        RequestReport requestReport = JSON.parseObject(report, RequestReport.class);
        ResponseReport responseReport = new ResponseReport();
        return responseReport.returnError("9999", "服务器繁忙,请稍后再试", requestReport);
    }

}

这里做个参考,该代码包含了基本配置和异常处理(这里只是打印了下日志)

1.3. 使用


@ResponseBody
@RequestMapping(value = "/rpc.api")
public ResponseReport doRemoteCall(@RequestParam(required = false) String report, OtherFields fields, HttpServletRequest request) {
    RpcCommand rpcCommand = new RpcCommand(tableService, applicationContextProvider, tokenService,
            report,fields,request);
    return rpcCommand.execute();
}

以上是关于HystrixCommand实战的主要内容,如果未能解决你的问题,请参考以下文章

微服务架构整理-(九SpringCloud实战之Hystrix [2])

深入浅出SpringCloud原理及实战「Netflix系列之Hystrix」针对于限流熔断组件Hystrix的Command创建和执行实现原理分析

solr分布式索引实战分片配置读取:工具类configUtil.java,读取配置代码片段,配置实例

Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段

golang代码片段(摘抄)

在@HystrixCommand 回退方法中获取失败异常