欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

HystrixCommand实战

程序员文章站 2024-03-20 22:11:22
...

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();
}