該協議主要是爲了支持老項目能夠消費springcloud提供的接口,並能夠利用dubbo的服務發現,構建出一個springboot rest集羣, dubbo與springboot結合時,不須要dubbo再次導出rest服務。而是由springboot提供rest服務dubbo端只負責註冊,構建服務目錄。git
Git: spring-boot-starter-dubbo Example: dubbo-demogithub
<!--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(); } }
@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(); }