All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.dubbo.rpc.protocol.feign.FeignProtocol Maven / Gradle / Ivy

There is a newer version: 1.5.33
Show newest version
package com.alibaba.dubbo.rpc.protocol.feign;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.protocol.AbstractProxyProtocol;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import feign.Feign;
import feign.RequestInterceptor;
import feign.Retryer;
import feign.Target;
import feign.codec.ErrorDecoder;
import feign.httpclient.ApacheHttpClient;
import feign.hystrix.HystrixFeign;
import feign.hystrix.SetterFactory;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.feign.support.SpringDecoder;
import org.springframework.cloud.netflix.feign.support.SpringEncoder;
import org.springframework.cloud.netflix.feign.support.SpringMvcContract;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.net.ssl.SSLContext;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

/**
 * Created by wuyu on 2016/4/6.
 */
public class FeignProtocol extends AbstractProxyProtocol {


    private static ObjectFactory objectFactory = new ObjectFactory() {
        @Override
        public HttpMessageConverters getObject() throws BeansException {
            return getApplicationContext().getBean(HttpMessageConverters.class);
        }
    };

    @Override
    protected  Runnable doExport(T impl, final Class type, URL url) throws RpcException {

        //不做任何操作,由springboot对外提供该服务,dubbo只负责注册服务
        return new Runnable() {
            @Override
            public void run() {
                RequestMappingHandlerMapping requestMappingHandlerMapping = getApplicationContext().getBean(RequestMappingHandlerMapping.class);
                Map handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
                for (Map.Entry entry : handlerMethods.entrySet()) {
                    Object bean = entry.getValue().getBean();
                    if (type.isAssignableFrom(AopUtils.getTargetClass(bean))) {
                        requestMappingHandlerMapping.unregisterMapping(entry.getKey());
                    }
                }
            }
        };
    }

    @Override
    protected  T doRefer(Class type, URL url) throws RpcException {

        int timeout = url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
        int connections = url.getParameter(Constants.CONNECTIONS_KEY, 20);
        int retries = url.getParameter(Constants.RETRIES_KEY, 0);


        String schema = "http://";
        if (url.getProtocol().equalsIgnoreCase("feigns")) {
            schema = "https://";
        }

        String api = schema + url.getHost() + ":" + url.getPort();

        FeignClient feignClient = type.getAnnotation(FeignClient.class);


        if (feignClient != null) {
            //如果feign注解携带url,将以url为准
            if (!StringUtils.isBlank(feignClient.url())) {
                api = getEnvironment().resolvePlaceholders(feignClient.url());
            }

            if (!StringUtils.isBlank(feignClient.path())) {
                api = api + ("/" + feignClient.path()).replaceAll("[/]+", "/");
            }
        }

        return target(api, type, connections, timeout, retries);
    }

    @Override
    public int getDefaultPort() {
        //该端口与springboot保持一致
        String port = getEnvironment().resolvePlaceholders("${server.port}");
        return !StringUtils.isBlank(port) ? Integer.parseInt(port) : 8080;
    }

    public static  T target(Class type) {
        return target(getEnvironment().resolvePlaceholders(type.getAnnotation(FeignClient.class).url()), type, 20, 3000, 0);
    }

    public static  T target(String url, Class type) {
        return target(url, type, 20, 3000, 0);
    }

    public static  T target(String url, Class type, final int connections, final int timeout, int retries) {
        SSLContext sslContext = SSLContexts.createSystemDefault();
        Registry socketFactoryRegistry = RegistryBuilder.create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", new SSLConnectionSocketFactory(sslContext))
                .build();

        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(timeout)
                .setSocketTimeout(timeout)
                .build();

        CloseableHttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManager(manager)
                .setDefaultRequestConfig(requestConfig)
                .setMaxConnPerRoute(connections)
                .setMaxConnTotal(connections)
                .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
                .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
                .build();

        FeignClient feignClient = type.getAnnotation(FeignClient.class);
        SetterFactory setterFactory = new SetterFactory() {
            @Override
            public HystrixCommand.Setter create(Target target, Method method) {
                String groupKey = target.name();
                String commandKey = Feign.configKey(target.type(), method);
                return HystrixCommand.Setter
                        .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
                        .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                                .withExecutionTimeoutInMilliseconds(timeout)
                                .withExecutionIsolationSemaphoreMaxConcurrentRequests(connections))
                        .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
            }
        };

        HystrixFeign.Builder builder = HystrixFeign.builder()
                .setterFactory(setterFactory)
                .requestInterceptors(getApplicationContext().getBeansOfType(RequestInterceptor.class).values())
                .contract(new SpringMvcContract())
                .encoder(new SpringEncoder(objectFactory))
                .decoder(new SpringDecoder(objectFactory))
                .client(new ApacheHttpClient(httpClient))
                .errorDecoder(new ErrorDecoder.Default())
                .retryer(new Retryer.Default(100, 500, retries));


        if (feignClient != null) {
            Class fallbackFactory = feignClient.fallbackFactory();
            if (void.class != fallbackFactory) {
                if (void.class != fallbackFactory) {
                    setterFactory = getApplicationContext().getBean(SetterFactory.class);
                }
            }

            builder.setterFactory(setterFactory);

            if (feignClient.decode404()) {
                builder.decode404();
            }

            Class fallback = feignClient.fallback();
            if (void.class != fallback) {
                try {
                    Map beansOfType = getApplicationContext().getBeansOfType(fallback);
                    if (beansOfType.size() > 0) {
                        return builder.target(type, url, (T) getApplicationContext().getBean(fallback));
                    }
                    return builder.target(type, url, (T) fallback.newInstance());

                } catch (Exception e) {
                    throw new RpcException(e);
                }
            }
        }
        return builder.target(type, url);
    }


    @SuppressWarnings("unchecked")
    private static WebApplicationContext getApplicationContext() {
        Field contextsFiled = ReflectionUtils.findField(SpringExtensionFactory.class, "contexts");
        contextsFiled.setAccessible(true);
        for (ApplicationContext applicationContext : (Set) ReflectionUtils.getField(contextsFiled, null)) {
            if (applicationContext instanceof WebApplicationContext) {
                return (WebApplicationContext) applicationContext;
            }
        }
        throw new RpcException("not found webApplicationContext!");
    }

    private static Environment getEnvironment() {
        return getApplicationContext().getEnvironment();
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy