Механизм взрывателя Spring Cloud Feign заполняет дыру

задняя часть Spring
Механизм взрывателя Spring Cloud Feign заполняет дыру

Оригинальная ссылка:woohoo.cipher magic.talent/весенне-уродливый…

проблема

В последнее время при разработке проектов, использующих сервисы вызовов FeIGN, при срабатывании механизма фьюза возникает проблема:

  • Аномалия Информация о форме:TestService#addRecord(ParamVO) failed and no fallback available.;
  • Исходная информация об исключении, созданная поставщиком услуг, не может быть получена;
  • Реализация некоторых бизнес-методов не входит в прерыватель цепи и напрямую вызывает исключение;

Далее вышеуказанные проблемы будут решаться одна за другой.

заfailed and no fallback available.Эта информация об исключении связана с тем, что проект открыт:

feign.hystrix.enabled: true

Выдает исключение при вызове службы, но не определяетfallbackметод, будет выдано указанное выше исключение. Это приводит к первому решению.

@FeignClientплюсfallbackметод и получить информацию об исключении

за@FeignClientукрашенный интерфейс плюсfallbackСуществует два способа использования этого метода.Поскольку информация об исключении должна быть получена, используйтеfallbackFactoryПуть:

@FeignClient(name = "serviceId", fallbackFactory = TestServiceFallback.class)
public interface TestService {

    @RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
    Result get(@PathVariable("id") Integer id);
    
}

существует@FeignClientуказано в комментарииfallbackFactory, в приведенном выше примереTestServiceFallback:

import feign.hystrix.FallbackFactory;
import org.apache.commons.lang3.StringUtils;

@Component
public class TestServiceFallback implements FallbackFactory<TestService> {

    private static final Logger LOG = LoggerFactory.getLogger(TestServiceFallback.class);

    public static final String ERR_MSG = "Test接口暂时不可用: ";

    @Override
    public TestService create(Throwable throwable) {
        String msg = throwable == null ? "" : throwable.getMessage();
        if (!StringUtils.isEmpty(msg)) {
            LOG.error(msg);
        }
        return new TestService() {
            @Override
            public String get(Integer id) {
                return ResultBuilder.unsuccess(ERR_MSG + msg);
            }
        };
    }
}

путем реализацииFallbackFactory, допустимыйcreateИсключение, выдаваемое службой, получено в методе.Но учтите, что здесь исключениеFeignОбернутое исключение, исключение, созданное исходным методом, нельзя увидеть непосредственно в информации об исключении. Информация об исключении, полученная в это время, выглядит следующим образом:

status 500 reading TestService#addRecord(ParamVO); content:
{"success":false,"resultCode":null,"message":"/ by zero","model":null,"models":[],"pageInfo":null,"timelineInfo":null,"extra":null,"validationMessages":null,"valid":false}

Чтобы объяснить, в этом примере возвращаемая интерфейсом информация поставщика услуг будет единообразно инкапсулирована в пользовательский класс.Result, содержание указано вышеcontent:

{"success":false,"resultCode":null,"message":"/ by zero","model":null,"models":[],"pageInfo":null,"timelineInfo":null,"extra":null,"validationMessages":null,"valid":false}

Таким образом, информация об исключении, которую я ожидаю,messageСодержание:/ by zero, чтобы исключение можно было легко определить при регистрации.

Сохранить исходную информацию об исключении

При вызове службы, если код состояния, возвращаемый службой, не равен 200, он войдет вFeignизErrorDecoder, поэтому, если мы хотим проанализировать информацию об исключении, нам нужно переписатьErrorDecoder:

import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;

/**
 * @Author: CipherCui
 * @Description: 保留 feign 服务异常信息
 * @Date: Created in 1:29 2018/6/2
 */
public class KeepErrMsgConfiguration {

    @Bean
    public ErrorDecoder errorDecoder() {
        return new UserErrorDecoder();
    }

    /**
     * 自定义错误解码器
     */
    public class UserErrorDecoder implements ErrorDecoder {

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

        @Override
        public Exception decode(String methodKey, Response response) {
            Exception exception = null;
            try {
                // 获取原始的返回内容
                String json = Util.toString(response.body().asReader());
                exception = new RuntimeException(json);
                // 将返回内容反序列化为Result,这里应根据自身项目作修改
                Result result = JsonMapper.nonEmptyMapper().fromJson(json, Result.class);
                // 业务异常抛出简单的 RuntimeException,保留原来错误信息
                if (!result.isSuccess()) {
                    exception = new RuntimeException(result.getMessage());
                }
            } catch (IOException ex) {
                logger.error(ex.getMessage(), ex);
            }
            return exception;
        }
    }

}

Вышеуказанное является примером.принцип основан наresponse.body()Десериализовать в пользовательскийResultкласс, извлечьmessageинформацию, затем бросаетRuntimeException, чтобы при входе в метод fuse полученное исключение было тем, которое мы обработалиRuntimeException.

Обратите внимание, что приведенные выше примеры не являются универсальными, но принципы одинаковы, и каждый должен вносить соответствующие модификации в соответствии со своими проектами.

Чтобы приведенный выше код работал, вам также необходимо@FeignClientуказано в комментарииconfiguration:

@FeignClient(name = "serviceId", fallbackFactory = TestServiceFallback.class, configuration = {KeepErrMsgConfiguration.class})
public interface TestService {

    @RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
    String get(@PathVariable("id") Integer id);
    
}

Не вводить фьюз, кидать исключение напрямую

Иногда мы не хотим, чтобы метод входил в логику автоматического выключателя, а просто выдавал исключение как есть. В этом случае нам нужно поймать только две точки:не входить в предохранитель,как есть.

Оригинал заключается в том, чтобы получить исходное исключение, которое было введено выше, без ввода предохранителя, исключение необходимо инкапсулировать какHystrixBadRequestException,заHystrixBadRequestException,FeignОн будет брошен напрямую, без ввода метода предохранителя.

Поэтому нам нужно толькоKeepErrMsgConfigurationНебольшую модификацию можно сделать на основе:

/**
 * @Author: CipherCui
 * @Description: feign 服务异常不进入熔断
 * @Date: Created in 1:29 2018/6/2
 */
public class NotBreakerConfiguration {

    @Bean
    public ErrorDecoder errorDecoder() {
        return new UserErrorDecoder();
    }

    /**
     * 自定义错误解码器
     */
    public class UserErrorDecoder implements ErrorDecoder {

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

        @Override
        public Exception decode(String methodKey, Response response) {
            Exception exception = null;
            try {
                String json = Util.toString(response.body().asReader());
                exception = new RuntimeException(json);
                Result result = JsonMapper.nonEmptyMapper().fromJson(json, Result.class);
                // 业务异常包装成 HystrixBadRequestException,不进入熔断逻辑
                if (!result.isSuccess()) {
                    exception = new HystrixBadRequestException(result.getMessage());
                }
            } catch (IOException ex) {
                logger.error(ex.getMessage(), ex);
            }
            return exception;
        }
    }

}

Суммировать

Чтобы лучше добиться эффекта плавких предохранителей, мы должны указать для каждого интерфейсаfallbackметод. В соответствии с собственными бизнес-характеристиками вышеперечисленное может быть гибко настроеноKeepErrMsgConfigurationиNotBreakerConfigurationили напишите свойConfiguration.

Приведенные выше примеры наполнены, поэтому, пожалуйста, не стесняйтесь посоветуйте. Я надеюсь, что вы можете получить что-то полезное из него и применить его к своим собственным проектам, спасибо за чтение.

Оригинальная ссылка:woohoo.cipher magic.talent/весенне-уродливый…