feign的fallback处理

HTTP请求异常Fallback容错机制,它是基于Hystrix实现的

feign.hystrix.enabled=true开启该功能

什么情况下会进入fallback?

服务方返回的response的status不为200

正常情况下当被调用服务返回response的status不等于200,就会进入到feign.codec.ErrorDecoder#decode方法中,这里会抛出一个RetryableException异常,自动进入fallback

源码feign.codec.ErrorDecoder#decode

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package feign.codec;

import feign.FeignException;
import feign.Response;
import feign.RetryableException;
import feign.Util;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public interface ErrorDecoder {
    Exception decode(String var1, Response var2);

    public static class RetryAfterDecoder {
        static final DateFormat RFC822_FORMAT;
        private final DateFormat rfc822Format;

        RetryAfterDecoder() {
            this(RFC822_FORMAT);
        }

        RetryAfterDecoder(DateFormat rfc822Format) {
            this.rfc822Format = (DateFormat)Util.checkNotNull(rfc822Format, "rfc822Format", new Object[0]);
        }

        protected long currentTimeMillis() {
            return System.currentTimeMillis();
        }

        public Date apply(String retryAfter) {
            if (retryAfter == null) {
                return null;
            } else if (retryAfter.matches("^[0-9]+$")) {
                long deltaMillis = TimeUnit.SECONDS.toMillis(Long.parseLong(retryAfter));
                return new Date(this.currentTimeMillis() + deltaMillis);
            } else {
                DateFormat var2 = this.rfc822Format;
                synchronized(this.rfc822Format) {
                    Date var10000;
                    try {
                        var10000 = this.rfc822Format.parse(retryAfter);
                    } catch (ParseException var5) {
                        return null;
                    }

                    return var10000;
                }
            }
        }

        static {
            RFC822_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        }
    }

    public static class Default implements ErrorDecoder {
        private final ErrorDecoder.RetryAfterDecoder retryAfterDecoder = new ErrorDecoder.RetryAfterDecoder();

        public Default() {
        }

        public Exception decode(String methodKey, Response response) {
            FeignException exception = FeignException.errorStatus(methodKey, response);
            Date retryAfter = this.retryAfterDecoder.apply((String)this.firstOrNull(response.headers(), "Retry-After"));
            return (Exception)(retryAfter != null ? new RetryableException(exception.getMessage(), exception, retryAfter) : exception);
        }

        private <T> T firstOrNull(Map<String, Collection<T>> map, String key) {
            return map.containsKey(key) && !((Collection)map.get(key)).isEmpty() ? ((Collection)map.get(key)).iterator().next() : null;
        }
    }
}

当我们需要重新定义服务调用者接收的异常,或者想要服务端抛出异常但是调用者不进入fallback就需要重写ErrorDecoder

下面重写是判断response status的状态并主动接收自定义异常ServiceException,当前业务需要服务端抛出异常后服务调用者不进入fallback,所以重写时抛出HystrixBadRequestException异常,feign会直接抛出,不进入熔断

@Slf4j
@Configuration
public class FeignExceptionConfiguration {
    @Bean
    public ErrorDecoder errorDecoder() {
        return new UserErrorDecoder();
    }
    /**
     * 重新实现feign的异常处理,捕捉restful接口返回的json格式的异常信息
     *
     */
    public class UserErrorDecoder implements ErrorDecoder {

        @Override
        public Exception decode(String methodKey, Response response) {
            Exception exception = null;
            ObjectMapper mapper = new ObjectMapper();
            //空属性处理
            mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
            try {
                String json = Util.toString(response.body().asReader());
                if (StringUtils.isEmpty(json)) {
                    return null;
                }
                JSONObject jsonObject = JSONObject.parseObject(json);
                FeignFaildResult result = new FeignFaildResult();
                result.setStatus((int)jsonObject.get("code"));
                result.setMessage(String.valueOf(jsonObject.get("msg")));


                if (result.getStatus() != HttpStatus.OK.value()) {
                    ServiceException serviceException = new ServiceException(new ServiceStatusCode.InternalServiceStatusCode(String.valueOf(jsonObject.get("errorCode")), result.getMessage(),new Object[0]));
                    exception = new HystrixBadRequestException("",serviceException);
                }
            } catch (IOException ex) {
                log.error(ex.getMessage(), ex);
            }
            return exception;
        }
    }
}

具体重写业务需要根据具体的使用场景来定,上面的重写试用于以下场景,即服务调用者希望服务反馈的异常不进入fallback,可以直接通过异常拦截将错误信息返给前端,那么就需要在统一 异常拦截增加对于HystrixBadRequestException异常的拦截,然后封装消息返回,具体如下:

if (ex instanceof HystrixBadRequestException) {
        HystrixBadRequestException hystrixBadRequestException = (HystrixBadRequestException) ex;
        if (hystrixBadRequestException.getCause() instanceof ServiceException) {
            ServiceException serviceException = (ServiceException) hystrixBadRequestException.getCause();
            return new Result().error(serviceException.getErrorCode().getCode(), serviceException.getErrorCode().getMessage());
    }
}

当服务之间调用,一般情况下大家还是希望如果服务报错,调用方就进入fallback,那么针对这种情况就需要我们再重新对ErrorDecoder进行重写。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值