zoukankan      html  css  js  c++  java
  • openfeign 使用方法和执行流程

    1.用法

    1.1引入依赖

    <!-- feign client -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
                <version>2.1.1.RELEASE</version>
            </dependency>

    1.3参数校验(利用MethodValidationInterceptor 再springContext中利用@Validated生成代理对象来进行参数校验)

    @Validated
    @FeignClient(name="nuts", url = "${nuts.sms.smsNoticeUrl}",configuration = {HttpClientProxyConfiguration.class})
    public interface NoticeSao {
    
        @PostMapping("/tesyt")
        @Async
        ResponseDTO sendSms(@Valid NoticeDTO noticeDTO, URI uri);
    }

    1.2 url配置的优化级 从高到低依次覆盖

    @FeignClient(name="nuts",2 url = "${nuts.sms.smsNoticeUrl}",configuration = {HttpClientProxyConfiguration.class})
    public interface NoticeSao {
    
        @PostMapping("/tesyt")
        @Async
        ResponseDTO sendSms(NoticeDTO noticeDTO,1 URI uri);
    }
    
    public void apply(RequestTemplate template) {
    //        3 template.target("http://test/test");
        }

    1.2.1 在参数中加上URI 

    1.3.2 @FeignClient 中的url带有http参数 

    1.4.3 在拦截器中使用 template.target("http://test/test");

    1.3配置拦截器 (注意拦截器使用范围)

    @Component
    @Slf4j
    public class NutsOpenApiInterceptor implements RequestInterceptor {

    1.4配置 HttpClientProxyConfiguration

    @FeignClient(name="nuts",3.url = "",configuration = {HttpClientProxyConfiguration.class})
    public interface NoticeSao {

    1.5遗留问题

    1.5.1 再集群中服务发现 和url 手动指定的矛盾化解

    2.原理

    2.1 启用配置类

    2.1.1 FeignAutoConfiguration

    2.1.2 @EnableFeignClients (FeignClientsRegistrar)

     FeignClientsRegistrar 的作用:

    1.注册默认的configuration,

    2.注册FeignClients即有@FeignClient注解的接口

    3.注册@FeignClient(name="nuts",3.url = "",configuration = {HttpClientProxyConfiguration.class})里的configuration到当前的content

    @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata,
                BeanDefinitionRegistry registry) {
            registerDefaultConfiguration(metadata, registry);
            registerFeignClients(metadata, registry);
        }

     2.2 初始FeignContext

        @Bean
        public FeignContext feignContext() {
            FeignContext context = new FeignContext();
            context.setConfigurations(this.configurations);
            return context;
        }
    public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
    
        public FeignContext() {
            super(FeignClientsConfiguration.class, "feign", "feign.client.name");
        }
    
    }

    为@FeignClient注解的类创建springContext,parentContext均为当前的springContxt;

     2.2 创建@FeignClient注解的接口的bean对象

    1.创建一个 FeignClientFactoryBean 在初始话时,注入各种需要的对象。

    2.需要注入接口的地方,会调用 FeignClientFactoryBean.getBean方法

    3.getBean中初始话builder Feign.Builder

        protected void configureUsingConfiguration(FeignContext context,
                Feign.Builder builder) {
            Logger.Level level = getOptional(context, Logger.Level.class);
            if (level != null) {
                builder.logLevel(level);
            }
            Retryer retryer = getOptional(context, Retryer.class);
            if (retryer != null) {
                builder.retryer(retryer);
            }
            ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
            if (errorDecoder != null) {
                builder.errorDecoder(errorDecoder);
            }
            Request.Options options = getOptional(context, Request.Options.class);
            if (options != null) {
                builder.options(options);
            }
            Map<String, RequestInterceptor> requestInterceptors = context
                    .getInstances(this.contextId, RequestInterceptor.class);
            if (requestInterceptors != null) {
                builder.requestInterceptors(requestInterceptors.values());
            }
    
            if (this.decode404) {
                builder.decode404();
            }
        }

    4.使用动态代理生成代理类  ReflectiveFeign

      public <T> T newInstance(Target<T> target) {
        Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
    
        for (Method method : target.type().getMethods()) {
          if (method.getDeclaringClass() == Object.class) {
            continue;
          } else if (Util.isDefault(method)) {
            DefaultMethodHandler handler = new DefaultMethodHandler(method);
            defaultMethodHandlers.add(handler);
            methodToHandler.put(method, handler);
          } else {
            methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
          }
        }
        InvocationHandler handler = factory.create(target, methodToHandler);
        T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
            new Class<?>[] {target.type()}, handler);
    
        for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
          defaultMethodHandler.bindTo(proxy);
        }
        return proxy;
      }

    5. 调用请求具体的方法 SynchronousMethodHandler

    @Override
      public Object invoke(Object[] argv) throws Throwable {
        RequestTemplate template = buildTemplateFromArgs.create(argv);
        Retryer retryer = this.retryer.clone();
        while (true) {
          try {
            return executeAndDecode(template);
          } catch (RetryableException e) {
            try {
              retryer.continueOrPropagate(e);
            } catch (RetryableException th) {
              Throwable cause = th.getCause();
              if (propagationPolicy == UNWRAP && cause != null) {
                throw cause;
              } else {
                throw th;
              }
            }
            if (logLevel != Logger.Level.NONE) {
              logger.logRetry(metadata.configKey(), logLevel);
            }
            continue;
          }
        }
      }

    问题:

    目前feign不支持 异步调用接收返回值

    下面写法是错误的,目前feign不支持这样写。

        @PostMapping("/")
        @Async
        Future<ResponseDTO> sendSms(NoticeDTO noticeDTO);

    采用另一种解决方案,在service中做异步

        @Async
        public Future<ResponseDTO> asyncSendSms(NoticeDTO noticeDTO){
            ResponseDTO responseDTO = noticeSao.sendSms(noticeDTO);
            return new AsyncResult(responseDTO);
        }

    用法:

    public class BspEncoder implements Encoder {
    
    
        private static final String CONTENT_TYPE_HEADER;
    
        private static final Pattern CHARSET_PATTERN;
    
        static {
            CONTENT_TYPE_HEADER = "Content-Type";
            CHARSET_PATTERN = Pattern.compile("(?<=charset=)([\w\-]+)");
        }
    
        @Value("${accessCode}")
        private String accessCode;
    
        @Value("${checkword}")
        private String checkword;
    
        private ContentProcessor processor=new UrlencodedFormContentProcessor();
    
        @Override
        public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
            //1.将object生成xml String
            com.sf.wms.sao.encoder.annotation.Request annotation = AnnotationUtils.findAnnotation((Class<?>) bodyType, com.sf.wms.sao.encoder.annotation.Request.class);
            Request request=new Request();
            request.setService(annotation.service());
            request.setLang(annotation.lang());
            request.setHead(accessCode);
            String xml="";
            try {
                xml= JAXBUtil.writeToString(request, (Class) bodyType, request.getClass());
            } catch (Exception e) {
                e.printStackTrace();
            }
            //instanceof 判断某个对象是否是某一类型
            // obj.getClass().isArray();
            //2.生成 verifyCode
            String verifyCode = md5AndBase64(xml + checkword);
    
            Map<String,Object> map=new HashMap<>();
            map.put("xml",xml);
            map.put("verifyCode",verifyCode);
    
            String contentTypeValue = getContentTypeValue(template.headers());
            val charset = getCharset(contentTypeValue);
            processor.process(template,charset,map);
        }
    
    
    
        private String getContentTypeValue (Map<String, Collection<String>> headers) {
            for (val entry : headers.entrySet()) {
                if (!entry.getKey().equalsIgnoreCase(CONTENT_TYPE_HEADER)) {
                    continue;
                }
                for (val contentTypeValue : entry.getValue()) {
                    if (contentTypeValue == null) {
                        continue;
                    }
                    return contentTypeValue;
                }
            }
            return null;
        }
    
        private Charset getCharset (String contentTypeValue) {
            val matcher = CHARSET_PATTERN.matcher(contentTypeValue);
            return matcher.find()
                    ? Charset.forName(matcher.group(1))
                    : UTF_8;
        }
    
        private static byte[] md5(String data) {
            return DigestUtils.md5(data);
        }
    
        private static String md5AndBase64(String data) {
            return base64Encode(md5(data));
        }
    
        private static String base64Encode(byte[] bytes) {
            return Base64.encodeBase64String(bytes);
        }
    }
    @FeignClient(name = "bsp", url = "${logisticsOrderUrl}",configuration = {BspConfiguration.class})
    public interface BspLogisticsOrderSao {
    
        @PostMapping(consumes = "application/x-www-form-urlencoded;charset=UTF-8")
        BspLogisticsOrderDTO getLogisticsOrder(LogisticsOrderListModel model);
    }
  • 相关阅读:
    [Everyday Mathematics]20150208
    [Everyday Mathematics]20150207
    [Everyday Mathematics]20150206
    数学基本技艺100题
    这天,白云酒楼里来了两位客人
    [Everyday Mathematics]20150205
    [Everyday Mathematics]20150204
    数学书籍阅读
    国科金发计〔2014〕86号
    [Everyday Mathematics]20150203
  • 原文地址:https://www.cnblogs.com/z-test/p/11572548.html
Copyright © 2011-2022 走看看