Spring Cloud Feign (chamada de serviço declarativa) explicação detalhada

Introdução

Feign pode ocultar a solicitação Rest e fingir ser um controlador semelhante ao Spring MVC. Não há necessidade de emendar urls, emendar parâmetros, etc. sozinho, e deixar tudo para o Feign.


Primeiro caso

  1. Dependências de importação em consumidores de serviço

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. @EnableFeignClientsAdicionar anotações à classe de inicialização

    O balanceamento de carga da fita foi integrado automaticamente no Feign, portanto, não há necessidade de defini-lo RestTemplatevocê mesmo

    @SpringCloudApplication
    @EnableFeignClients		// 开启Feign注解
    public class ConsumerApplication {
          
          
        public static void main(String[] args) {
          
          
            SpringApplication.run(ConsumerApplication.class, args);
        }
    }
    
  3. Escreva um cliente Feign

    @FeignClient(value = "user-service")    // 添加FeignClient,指定服务ID
    public interface UserClient {
          
          
        /**
         * 声明一个feign的接口,它的实现是服务提供者的controller实现
         */
        @GetMapping("/user/{id}")
        User getById(@PathVariable("id") Long id);
    }
    
  4. Chamado no código, use userClient para acessar:

    @Autowired  // 注入UserClient
    private UserClient userClient;
    
    public User getUserById(@PathVariable long id) {
          
          
        User user = userClient.getById(id);
        return user;
    }
    

Explicação detalhada da anotação @FeignClient

  • A anotação @FeignClient só pode ser utilizada na interface Interface. Feign irá gerar classes de implementação através de um proxy dinâmico.

    • A anotação FeignClient é modificada por @Target(ElementType.TYPE), indicando que o destino da anotação FeignClient está na interface
  • @FeignClient , declare que este é um cliente Feign e especifique o nome do serviço por meio do atributo name / value

  • O método definido na interface adota totalmente as anotações do SpringMVC, e o Feign gerará URLs de acordo com as anotações e acesso para obter os resultados

  • A classe de inicialização do serviço deve ser anotada com @EnableFeignClients para que o Fegin entre em vigor


Os atributos comuns da anotação @FeignClient são os seguintes:

  • name / value : Especifique o nome de FeignClient. Se o projeto usar Ribbon (registro), o atributo name será usado como o nome do microsserviço para descoberta de serviço
  • url : Geralmente usado para depuração, você pode especificar manualmente o endereço chamado por @FeignClient. vazio por padrão
    • A url pode ser obtida no arquivo de configuração, se houver será chamada através da url, caso contrário será chamada de acordo com o nome do serviço. formato éurl = "${xxx.xxx.xxx: }"
  • configuração : Classe de configuração do Feign, você pode personalizar o Codificador, Decodificador, LogLevel, Contrato do Feign, e você pode especificar configurações diferentes para cada cliente do Feign
  • fallback : define uma classe de processamento tolerante a falhas. Quando a chamada de uma interface remota falha ou atinge o tempo limite, a lógica tolerante a falhas da interface correspondente será invocada. A classe especificada por fallback deve implementar a interface marcada por @FeignClient
  • fallbackFactory : classe de fábrica, usada para gerar exemplos de classe de fallback, através deste atributo, a lógica tolerante a falhas comum de cada interface pode ser realizada e o código duplicado pode ser reduzido
  • path : Define o prefixo unificado do FeignClient atual, usado quando server.context-path, server.servlet-path são configurados no projeto
  • decode404 : Quando ocorrer um erro http 404, se o campo for verdadeiro, o decodificador será chamado para decodificação, caso contrário, uma FeignException será lançada

Método de chamada:

  • Método 1: O provedor de interface está no centro de registro

    Se o provedor de serviços estiver registrado no centro de registro, o valor de nome ou valor será: o nome do serviço do provedor de serviços. Um nome ou valor deve ser especificado para todos os clientes

    @FeignClient(value="run-product",fallback = ProductClientServiceFallBack.class)
    
  • Método 2: Uma única interface http, o provedor de interface não está registrado no centro de registro

    @FeignClient(name="runClient11111",url="localhost:8001")
    

    O valor de name aqui é: o nome do cliente chamador

Os dois métodos acima podem ser chamados normalmente. name pode ser o nome do serviço do registro e, quando o atributo url é adicionado, o valor do nome não tem nada a ver com o nome do serviço do registro.


Configuração do Feign Client

A configuração simulada é estendida com base na configuração da faixa de opções e pode oferecer suporte à configuração de tempo limite de nível de serviço, portanto, a configuração simulada e a configuração da faixa de opções têm o mesmo efeito.

A ordem de prioridade do SpringCloud para configuração é a seguinte:

  • Simular configuração local > Simular configuração global > Configuração local da faixa de opções > Configuração global da faixa de opções
  • A ordem de prioridade dos atributos do arquivo de configuração e das classes de configuração é: configuração do atributo do arquivo de configuração > configuração do código da classe de configuração
feign:
  client:
    config:
      default:	# 全部服务配置
        connectTimeout: 5000	# 建立连接的超时时长,单位:毫秒。默认为1000
        readTimeout: 5000		# 指建立连接后从服务端读取到可用资源所用的超时时间,单位:毫秒。默认为1000
        loggerLevel: FULL		# 日志级别
        errorDecoder: com.example.SimpleErrorDecoder  # Feign的错误解码器,相当于代码配置方式中的ErrorDecoder
        retryer: com.example.SimpleRetryer  # 配置重试,相当于代码配置方式中的Retryer
        requestInterceptors: # 配置拦截器,相当于代码配置方式中的RequestInterceptor
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        decode404: false	# 是否对404错误解码
        encode: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        contract: com.example.SimpleContract
      serverName:	# 单独给某⼀服务配置。serverName是服务名,使⽤的时候要⽤服务名替换掉这个
        connectTimeout: 5000
        readTimeout: 5000

Fingir solicitações para adicionar cabeçalhos

Solução 1: adicione informações de cabeçalhos à anotação @RequestMapping no método

O atributo da anotação @RequestMapping contém uma matriz de cabeçalhos. Você pode adicionar os cabeçalhos necessários à anotação @RequestMapping no método especificado, que pode ser embutido em código ou configuração de leitura =

Da mesma forma, as anotações @PostMapping e @GetMapping do grupo @RequestMapping são aplicáveis

@FeignClient(name = "server",url = "127.0.0.1:8080")
public interface FeignTest {
    
    
    @RequestMapping(value = "/test",headers = {
    
    "app=test-app","token=${test-app.token}"})
    String test();
}

Solução 2: adicione informações de cabeçalhos à anotação @RequestMapping na interface

Se todos os métodos na mesma interface exigirem os mesmos cabeçalhos, você poderá adicionar cabeçalhos à anotação @RequestMapping na interface para que os métodos de toda a interface sejam adicionados com os mesmos cabeçalhos

@FeignClient(name = "server",url = "127.0.0.1:8080")
@RequestMapping(value = "/",headers = {
    
    "app=test-app","token=${test-app.token}"})
public interface FeignTest {
    
    
    @RequestMapping(value = "/test")
    String test();
}

Solução 3: use a anotação @Headers para adicionar informações de cabeçalhos (não recomendado)

@FeignClient(name = "server",url = "127.0.0.1:8080")
@Headers({
    
    "app: test-app","token: ${test-app.token}"})
public interface FeignTest {
    
    
    @RequestMapping(value = "/test")
    String test();
}

Olhando para a documentação oficial do openfeign, descobriu-se que ele usa @Headers para adicionar cabeçalhos. O teste descobriu que não teve efeito . A nuvem Spring usa seu próprio SpringMvcContract para analisar anotações, então você precisa implementar um contrato você mesmo para dar suporte às anotações @Headers. Para implementação específica, consulte (https://juejin.im/post/6844903961653149709)


Solução 4: personalize o RequestInterceptor para adicionar informações de cabeçalho

Feign fornece uma interface interceptadora RequestInterceptor, que pode interceptar solicitações feign implementando a interface RequestInterceptor. A interface fornece um método apply() para implementar o método apply()

A implementação do método apply() para adicionar cabeçalhos diretamente interceptará todas as solicitações e adicionará cabeçalhos. Se nem todas as solicitações falsas precisarem ser usadas, esse método não é recomendado

@Component
public class FeignRequestInterceptor implements RequestInterceptor {
    
    

    @Value("${test-app.token}")
    private String token;
    
    @Override
    public void apply(RequestTemplate requestTemplate) {
    
    
        requestTemplate.header("app","test-app");//静态
        requestTemplate.header("token",token);//读配置
    }
}

Solução 5: Personalize a implementação do RequestInterceptor para adicionar dados dinâmicos ao cabeçalho

Nenhuma das soluções acima é adequada para colocar dados dinâmicos em cabeçalhos. Em cenários gerais, informações dinâmicas como assinaturas calculadas e IDs de usuário podem precisar ser definidas em cabeçalhos, portanto, uma solução mais completa é necessária. O esquema 1/2/3 não pode definir um valor dinâmico e o esquema 4 pode definir um valor dinâmico, mas não faz distinção entre as solicitações. Portanto, o esquema 5 é aprimorado com base no esquema 4. A implementação específica é a seguinte:

No código de chamada da solicitação, obtenha o objeto HttpServletRequest, encapsule o valor que precisa ser adicionado aos cabeçalhos em um mapa e coloque-o no campo de atributo de HttpServletRequest

    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
    String signedMsg = getSignedMsg(reqJson); 		// 计算签名字符串
    Map<String, String> reqMap = new HashMap<>();
    reqMap.put("content-type", "application/json");	//常量字段
    reqMap.put("accessKey", accessKey);		//常量字段
    reqMap.put("signedMsg", signedMsg);		//动态计算/获取字段
    request.setAttribute("customizedRequestHeader", reqMap);

Obtenha a chave especificada no campo de atributo do objeto HttpServletRequest no RequestInterceptor personalizado e inclua todos os parâmetros no mapa correspondente à chave nos cabeçalhos.

@Component
public class FeignRequestInterceptor implements RequestInterceptor {
    
    

    @Override
    public void apply(RequestTemplate requestTemplate) {
    
    
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 设置自定义header
        // 设置request中的attribute到header以便转发到Feign调用的服务
        Enumeration<String> reqAttrbuteNames = request.getAttributeNames();
        if (reqAttrbuteNames != null) {
    
    
            while (reqAttrbuteNames.hasMoreElements()) {
    
    
                String attrName = reqAttrbuteNames.nextElement();
                if (!"customizedRequestHeader".equalsIgnoreCase(attrName)) {
    
    
                    continue;
                }
                Map<String,String> requestHeaderMap = (Map)request.getAttribute(attrName);
                for (Map.Entry<String, String> entry : requestHeaderMap.entrySet()) {
    
    
                    requestTemplate.header(entry.getKey(), entry.getValue());
                }
                break;
            }
        }
    }
}

Balanceamento de carga (faixa de opções)

O próprio Feign integrou as dependências do Ribbon e a configuração automática, e suporta o Ribbon por padrão.

A faixa de opções integrada do Fegin define o tempo limite da solicitação por padrão, que é de 1000ms por padrão. Como há um mecanismo de repetição dentro da faixa de opções, assim que o tempo expirar, a solicitação será reiniciada automaticamente

Pode ser modificado por configuração:

A configuração global usa ribbon.=

ribbon:
  ReadTimeout: 2500 	# 数据通信超时时长,单位:ms。默认为1000
  ConnectTimeout: 500 	# 连接超时时长,单位:ms。默认为1000
  OkToRetryOnAllOperations: false 	# 是否对所有的异常请求(连接异常和请求异常)都重试。默认为false
  MaxAutoRetriesNextServer: 1 		# 最多重试多少次连接服务(实例)。默认为1。不包括首次调用
  MaxAutoRetries: 0 	# 服务的单个实例的重试次数。默认为0。不包括首次调用
  NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule	# 切换负载均衡策略为随机

Especifique a configuração do serviço <nome do serviço>.ribbon.=

serverName:	# 单独给某⼀服务配置。serverName是服务名,使⽤的时候要⽤服务名替换掉这个
  ribbon:
    connectTimeout: 5000
    readTimeout: 5000

mecanismo de tolerância a falhas

suporte Hystrix

O Feign também possui integração com o Hystix por padrão, mas está desativado por padrão. Ele precisa ser habilitado com os seguintes parâmetros:

feign:
  hystrix:
    enabled: true	# 开启hystrix熔断机制
     
hystrix:
  command:
    default:	# 全局默认配置
      execution:	# 线程隔离相关
        timeout:
          enabled: true		# 是否给方法执行设置超时时间,默认为true。一般不改。
        isolation:
          strategy: THREAD	# 配置请求隔离的方式,这里是默认的线程池方式。还有一种信号量的方式semaphore,
          thread:
            timeoutlnMilliseconds: 10000	# 方式执行的超时时间,默认为1000毫秒,在实际场景中需要根据情况设置
      circuitBreaker:	# 服务熔断相关
        requestVolumeThreshold: 10			# 触发熔断的最小请求次数,默认20
        sleepWindowInMilliseconds: 10000	# 休眠时长,单位毫秒,默认是5000毫秒
        errorThresholdPercentage: 50		# 触发熔断的失败请求最小占比,默认50%
    serverName:	# 单独给某⼀服务配置
      execution:
        timeout:
          enabled: true
        isolation:
          strategy: THREAD
          thread:
            timeoutlnMilliseconds: 10000

Perceber:

  • O período de tempo limite do Hystix deve ser maior do que o tempo total de repetição da Faixa de opções. Caso contrário, após o tempo limite do comando Hystrix, o comando será fundido diretamente e o mecanismo de repetição não terá significado.

    Faixa de opções: número total de tentativas = número de servidores acessados ​​* número máximo de tentativas para um único servidor

    Ou seja, o número total de novas tentativas = (1+MaxAutoRetriesNextServer)*(1+MaxAutoRetries)

    Tempo limite do Hystrix > (soma do tempo limite da faixa de opções) * número de novas tentativas

    Portanto, sugere-se que o período de timeout do histrix seja:

    ( ( 1+MaxAutoRetriesNextServer) * (1+MaxAutoRetries ) ) * (ReadTimeout + connectTimeout)

    • MaxAutoRetries: Ribbon Configuration: O número de novas tentativas para uma única instância do serviço. Não inclui primeira chamada
    • MaxAutoRetriesNextServer: Ribbon configuration: Quantas vezes repetir o serviço de conexão (instância) no máximo. Não inclui primeira chamada
    • ReadTimeout: Configuração da fita: tempo limite de comunicação
    • connectTimeout: Configuração da faixa de opções: tempo limite de estabelecimento da conexão

Suporte sentinela

O Sentinel pode fornecer a função de isolamento de semáforo por meio do controle de fluxo do número de modo de encadeamentos simultâneos. E combinado com o modo de rebaixamento do fusível baseado no tempo de resposta, ele pode rebaixar automaticamente quando o tempo médio de resposta de recursos instáveis ​​é relativamente alto, evitando que muitas chamadas lentas ocupem o número simultâneo e afetem todo o sistema.

confiar

        <!--Sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

Habilite o suporte do Feign para Sentinel no arquivo de configuração

feign:
  sentinel:
    enabled: true

Como usar o Feign após habilitar o suporte ao mecanismo tolerante a falhas

Após o Feign habilitar o suporte de mecanismo tolerante a falhas Hystrix ou Sentinel, ele pode ser usado das duas maneiras a seguir:

  • Solução 1: herdar diretamente a interface tolerante a falhas e implementar uma solução tolerante a falhas para cada método
  • Solução 2: implemente a interface FallbackFactory

Solução 1: herdar diretamente a interface tolerante a falhas e implementar uma solução tolerante a falhas para cada método

  1. Defina uma classe como a classe de processamento de fallback. Herde diretamente a interface tolerante a falhas e implemente uma solução tolerante a falhas para cada método

    @Component
    public class UserClientFallback implements UserClient {
          
          
        @Override
        public User getById(Long id) {
          
          
            return new User(1L, "我是备份-feign", 18, new Date());
        }
    }
    
  2. Use o atributo fallback na anotação @FeignClient para especificar uma classe de processamento tolerante a falhas customizada

    @FeignClient(value = "user-service",fallback = UserClientFallback.class)
    public interface UserClient {
          
          
        @GetMapping("/user/{id}")
        User getById(@PathVariable("id") Long id);
    }
    
  3. verificação de teste

    insira a descrição da imagem aqui


Solução 2: implemente a interface FallbackFactory. Você pode obter informações de erro de serviço específicas, o que é conveniente para solução de problemas posterior

@FeignClient(value="34-SPRINGCLOUD-SERVICE-GOODS", fallbackFactory = GoodsRemoteClientFallBackFactory.class)
public interface GoodsRemoteClient {
    
    
    
    @RequestMapping("/service/goods")
    public ResultObject goods();
}
@Component
public class GoodsRemoteClientFallBackFactory implements FallbackFactory<GoodsRemoteClient> {
    
    

    @Override
    public GoodsRemoteClient create(Throwable throwable) {
    
    
        return new GoodsRemoteClient() {
    
    
            @Override
            public ResultObject goods() {
    
    
                String message = throwable.getMessage();	// message即为错误信息
                System.out.println("feign远程调用异常:" + message);
                return new ResultObject();
            }
        };
    }
}

compactação de solicitação (feign.compression)

O Spring Cloud Feign oferece suporte à compactação GZIP para solicitações e respostas para reduzir a perda de desempenho durante a comunicação. A função de compressão de pedidos e respostas pode ser habilitada pelos seguintes parâmetros:

feign:
  compression:
    request:
      enabled: true
    response:
      enabled: true

Você também pode definir o tipo de dados da solicitação e o limite inferior do tamanho que aciona a compactação. Somente as solicitações que excederem esse tamanho serão compactadas:

feign:
  compression:
    request:
      enabled: true
      mime-types: text/xml,application/xml,application/json
      min-request-size: 2048

nível de registro

Passe logging.level.xx=debugpara definir o nível de log. No entanto, isso não tem efeito no cliente Fegin. Como @FeignCliento cliente modificado pela anotação criará uma nova instância de Fegin.Logger quando estiver em proxy, é necessário especificar o nível desse log adicionalmente.

Feign suporta 4 níveis de log:

  • NONE: Não registra nenhuma informação de log, que é o valor padrão.
  • BÁSICO: Registre apenas o método de solicitação, URL, código de status de resposta e tempo de execução
  • CABEÇALHOS: Com base no BASIC, as informações do cabeçalho da solicitação e resposta são gravadas adicionalmente
  • COMPLETO: registre detalhes de todas as solicitações e respostas, incluindo informações de cabeçalho, corpo da solicitação e metadados.

configuração global

Método 1: implementação das propriedades do arquivo de configuração

feign:
  client:
    config:
      default:	# 将调用的微服务名称设置为default即为配置成全局
        loggerLevel: FULL

Método 1: implementação de código

//在启动类上为@EnableFeignClients注解添加defaultConfiguration配置
@EnableFeignClients(defaultConfiguration = FeignConfig.class)

Refinado (especificar a configuração do serviço)

Método 1: implementação do arquivo de configuração

feign:
  client:
    config:
      server-1:		# 想要调用的微服务名称
        loggerLevel: FULL

Método 2: implementação de código

1) Escreva uma classe de configuração para definir o nível de log

@Configuration
public class FeignConfig {
    
    
    @Bean
    Logger.Level level(){
    
    
        return Logger.Level.FULL;
    }
}

2) Especifique a classe de configuração em FeignClient: (pode ser omitido)

@FeignClient(value = "user-service", configuration = FeignConfig.class)
// 添加FeignClient,指定服务ID
public interface UserClient {
    
    
    @GetMapping("/user/{id}")
    User getById(@PathVariable("id") Long id);
}

Exemplo de log simulado para cada visita:

insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/footless_bird/article/details/125341786
Recomendado
Clasificación