Dubbo兼容Springcloud發佈組件。Feign 插件,使Dubbo能夠消費spring cloud 服務,同時構建基於dubbo的rest服務

該協議主要是爲了支持老項目能夠消費springcloud提供的接口,並能夠利用dubbo的服務發現,構建出一個springboot rest集羣, dubbo與springboot結合時,不須要dubbo再次導出rest服務。而是由springboot提供rest服務dubbo端只負責註冊,構建服務目錄。git

Git: spring-boot-starter-dubbo Example: dubbo-demogithub

###依賴jar包

<!--Feign 支持-->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

###核心代碼

public class FeignProtocol extends AbstractProxyProtocol {


    private static ObjectFactory<HttpMessageConverters> objectFactory = new ObjectFactory<HttpMessageConverters>() {
        [@Override](https://my.oschina.net/u/1162528)
        public HttpMessageConverters getObject() throws BeansException {
            return getApplicationContext().getBean(HttpMessageConverters.class);
        }
    };

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

        //不作任何操做,由springboot對外提供該服務,dubbo只負責註冊服務
        return new Runnable() {
            [@Override](https://my.oschina.net/u/1162528)
            public void run() {
                RequestMappingHandlerMapping requestMappingHandlerMapping = getApplicationContext().getBean(RequestMappingHandlerMapping.class);
                Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
                for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
                    Object bean = entry.getValue().getBean();
                    if (type.isAssignableFrom(AopUtils.getTargetClass(bean))) {
                        requestMappingHandlerMapping.unregisterMapping(entry.getKey());
                    }
                }
            }
        };
    }

    protected <T> T doRefer(Class<T> 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.getPort() == 443 || url.getPort() == 8433) {
            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);
    }


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

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

    public static <T> T target(String url, Class<T> type, int connections, int timeout, int retries) {
        SSLContext sslContext = SSLContexts.createSystemDefault();
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>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, true))
                .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
                .build();
        return HystrixFeign.builder()
                .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))
                .target(type, url);
    }

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

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

}

###exmaple

@Value("${server.port}")
private int port;

//服務端,多協議發佈服務
[@Bean](https://my.oschina.net/bean)
public ServiceBean<UserService> userServiceServiceBean(@Autowired UserService userService) {
    ServiceBean<UserService> serviceBean = new ServiceBean<UserService>();
    serviceBean.setInterface(UserService.class);
    serviceBean.setRef(userService);
    serviceBean.setProtocols(Arrays.asList(new ProtocolConfig("dubbo"), new ProtocolConfig("feign", port)));
    return serviceBean;
}

[@Bean](https://my.oschina.net/bean)
//消費端,此種方式能夠避免使用@Reference註解,保持與spring註解一致
public ReferenceBean<UserService> userService() {
    ReferenceBean<UserService> bean = new ReferenceBean<UserService>();
    bean.setInterface(UserService.class);
    return bean;
}

###接口

@FeignClient(path = "/user")
public interface UserService {

    @RequestMapping(value = "{id}", method = RequestMethod.GET)
    User findOne(@PathVariable(value = "id") Integer id);

    @RequestMapping(value = "{id}", method = RequestMethod.DELETE)
    void delete(@PathVariable(value = "id") Integer id);

    @RequestMapping(value = "/", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    void save(@RequestBody User user);

    @RequestMapping(value = "/", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
    void update(@RequestBody User user);

    @RequestMapping(value = "/findAll", method = RequestMethod.GET)
    List<User> findAll();

}
相關文章
相關標籤/搜索