傳統Java Web非Spring Boot項目從Spring Cloud Eureka中獲取服務


更新日志:
2018/3/3 21:05:43 新建
2018/3/11 7:11:59 新增注冊到Eureka、從Eureka注銷、新增Feign,更新配置文件,更新代碼

部門項目的技術框架從 ZooKeeper+Dubbo 轉型為Spring Cloud 微服務,轉型順利、開發方便、使用良好,於是完全廢棄了ZooKeeper+Dubbo,而Web端后台管理界面的項目由於種種原因不希望大規模重構為Spring Boot項目,繼續保持原有的SSM框架,並使用http調用微服務接口。為避免將微服務地址寫死,這就需要Web項目連接到Spring Cloud Eureka 上通過服務名獲取微服務真實地址。

項目依賴

	<!-- eureka 服務發現 -->
	<dependency>
		<groupId>com.netflix.eureka</groupId>
		<artifactId>eureka-client</artifactId>
		<version>1.7.0</version>
	</dependency>
	<!-- Ribbon 負載均衡 -->
	<dependency>
		<groupId>com.netflix.ribbon</groupId>
		<artifactId>ribbon-core</artifactId>
		<version>${netflix.ribbon.version}</version>
	</dependency>
    <dependency>
        <groupId>com.netflix.ribbon</groupId>
        <artifactId>ribbon-loadbalancer</artifactId>
        <version>${netflix.ribbon.version}</version>
        <exclusions>  
        	<exclusion>  
        		<groupId>io.reactivex</groupId>
				<artifactId>rxjava</artifactId>  
    		</exclusion>
    	</exclusions>    
    </dependency>
    <dependency>
        <groupId>com.netflix.ribbon</groupId>
        <artifactId>ribbon-eureka</artifactId>
        <version>${netflix.ribbon.version}</version>
    </dependency>
	<!-- Feign 包裝http請求 -->
	<dependency>
		<groupId>io.github.openfeign</groupId>
		<artifactId>feign-hystrix</artifactId>
		<version>${netflix.feign.version}</version>
	</dependency>
	<dependency>
		<groupId>io.github.openfeign</groupId>
		<artifactId>feign-ribbon</artifactId>
		<version>${netflix.feign.version}</version>
	</dependency>
	<!-- <dependency>
		<groupId>io.github.openfeign</groupId>
		<artifactId>feign-gson</artifactId>
		<version>${netflix.feign.version}</version>
	</dependency> -->
	<dependency>
		<groupId>io.github.openfeign</groupId>
		<artifactId>feign-slf4j</artifactId>
		<version>${netflix.feign.version}</version>
	</dependency>
	<dependency>
		<groupId>io.github.openfeign</groupId>
		<artifactId>feign-jackson</artifactId>
		<version>${netflix.feign.version}</version>
	</dependency>
	
	<dependency>
		<groupId>io.reactivex</groupId>
		<artifactId>rxjava</artifactId>
		<version>1.1.1</version>
	</dependency>

這里使用netflix項目下的eureka、ribbon、Feign、Hystrix。
ribbon-開頭的項目都是同一個版本號,所以就抽取出${netflix.ribbon.version}統一管理。feign-開頭的項目也都是同一個的版本號,抽取${netflix.feign.version}統一管理。

需要注意:

  1. Maven依賴jar包沖突問題:rxjava項目在ribbon-loadbalancerfeign-hystrix依賴的hystrix-core中都有使用。當前最新版2.2.4的ribbon-loadbalancer使用rxjava:1.0.9。而feign-hystrix依賴的hystrix-core使用rxjava:1.1.1。因為依賴沖突,ribbon-loadbalancer中的rxjava:1.0.9代替掉了hystrix-core中的rxjava:1.1.1。這樣當程序運行時會瘋狂報找不到類Error,找不到rx/Single,這個類在2.0.9中並沒有,2.1.1中有hystrix-core用到了,但是由於依賴沖突使用2.0.9的rxjava沒有該類,所以報錯。
    解決辦法:ribbon-loadbalancer使用 exclusion 排除依賴 rxjava 即可。
  2. feign-core在中央倉庫有兩個groupId: com.netflix.feignio.github.openfeign 。groupIdcom.netflix.feign在2016年7月提交到8.18.0后就沒有再提交,而groupIdio.github.openfeign已經在2018年三月份提交到9.6.0。

配置文件

Ribbon配置

# ribbon.properties
# xxx-service對應的微服務名
xxx-service.ribbon.DeploymentContextBasedVipAddresses=xxx-service
# 固定寫法,xxx-service使用的ribbon負載均衡器
xxx-service.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList
# 每分鍾更新xxx-service對應服務的可用地址列表
xxx-service.ribbon.ServerListRefreshInterval=60000

Eureka配置

Eureka默認在classpath中尋找eureka-client.properties配置文件

# 控制是否注冊自身到eureka中,本項目雖然不對外提供服務,但需要Eureka監控,在Eureka列表上顯示
eureka.registration.enabled=true
# eureka相關配置
# 默認為true,以實現更好的基於區域的負載平衡。
eureka.preferSameZone=true
# 是否要使用基於DNS的查找來確定其他eureka服務器
eureka.shouldUseDns=false
# 由於shouldUseDns為false,因此我們使用以下屬性來明確指定到eureka服務器的路由(eureka Server地址)
eureka.serviceUrl.default=http://username:password@localhost:8761/eureka/
eureka.decoderName=JacksonJson
# 客戶識別此服務的虛擬主機名,這里指的是eureka服務本身
eureka.vipAddress=XXXplatform
#服務指定應用名,這里指的是eureka服務本身
eureka.name=XXXlatform
#服務將被識別並將提供請求的端口
eureka.port=8080

初始化Ribbon、注冊Eureka

之前初始化Ribbon、Eureka、注冊到Eureka和獲取地址的方法寫在靜態代碼塊和靜態方法中,這樣在項目停止時無法從Eureka中取消注冊,這會使Eureka進入安全模式,死掉的項目一直顯示在Eureka列表中。

繼承ServletContextListener,重寫contextInitialized、contextDestroyed,在應用上下文啟動時初始化Ribbon、Eureka、注冊到Eureka,應用上下文銷毀時注銷Eureka。

import java.io.IOException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.config.ConfigurationManager;
import com.netflix.discovery.DefaultEurekaClientConfig;
import com.netflix.discovery.DiscoveryManager;

/**
 * @ClassName: EurekaInitAndRegisterListener
 * @Description:  服務器啟動初始化Ribbon和注冊到Eureka Server
 * @author SuXun
 * @date 2018年3月7日 上午9:21:12
 */
@SuppressWarnings("deprecation")
public class EurekaInitAndRegisterListener implements ServletContextListener {
	private static final Logger LOGGER = LoggerFactory.getLogger(EurekaInitAndRegisterListener.class);
	/**
	 * 默認的ribbon配置文件名, 該文件需要放在classpath目錄下
	 */
	public static final String RIBBON_CONFIG_FILE_NAME = "ribbon.properties";

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		LOGGER.info("開始初始化ribbon");
		try {
			// 加載ribbon配置文件
			ConfigurationManager.loadPropertiesFromResources(RIBBON_CONFIG_FILE_NAME);
		} catch (IOException e) {
			e.printStackTrace();
			LOGGER.error("ribbon初始化失敗");
			throw new IllegalStateException("ribbon初始化失敗");
		}
		LOGGER.info("ribbon初始化完成");
		// 初始化Eureka Client
		LOGGER.info("Eureka初始化完成,正在注冊Eureka Server");
		DiscoveryManager.getInstance().initComponent(new MyInstanceConfig(), new DefaultEurekaClientConfig());
		ApplicationInfoManager.getInstance().setInstanceStatus(InstanceInfo.InstanceStatus.UP);
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		DiscoveryManager.getInstance().shutdownComponent();
	}
}

這里有個自定義的類MyInstanceConfig,這個類作用是將注冊到Eureka的hostName從主機名換成IP地址加端口號的形式。

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Enumeration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.appinfo.MyDataCenterInstanceConfig;

/**
 * @ClassName: MyInstanceConfig
 * @Description:  
 * @author SuXun
 * @date 2018年3月7日 上午9:33:31
 */
public class MyInstanceConfig extends MyDataCenterInstanceConfig {
	private static final Logger LOG = LoggerFactory.getLogger(MyInstanceConfig.class); 
	@Override
	public String getHostName(boolean refresh) {

		try {
			return findFirstNonLoopbackAddress().getHostAddress();
		} catch (Exception e) {
			return super.getHostName(refresh);
		}
	}
	
	public InetAddress findFirstNonLoopbackAddress() {
		InetAddress result = null;
		try {
			int lowest = Integer.MAX_VALUE;
			for (Enumeration<NetworkInterface> nics = NetworkInterface
					.getNetworkInterfaces(); nics.hasMoreElements();) {
				NetworkInterface ifc = nics.nextElement();
				if (ifc.isUp()) {
					LOG.trace("Testing interface: " + ifc.getDisplayName());
					if (ifc.getIndex() < lowest || result == null) {
						lowest = ifc.getIndex();
					}
					else if (result != null) {
						continue;
					}

					// @formatter:off
						for (Enumeration<InetAddress> addrs = ifc
								.getInetAddresses(); addrs.hasMoreElements();) {
							InetAddress address = addrs.nextElement();
							if (address instanceof Inet4Address
									&& !address.isLoopbackAddress()) {
								LOG.trace("Found non-loopback interface: "
										+ ifc.getDisplayName());
								result = address;
							}
						}
					// @formatter:on
				}
			}
		}
		catch (IOException ex) {
			LOG.error("Cannot get first non-loopback address", ex);
		}

		if (result != null) {
			return result;
		}

		try {
			return InetAddress.getLocalHost();
		}
		catch (UnknownHostException e) {
			LOG.warn("Unable to retrieve localhost");
		}

		return null;
	}
}

雖然DiscoveryManager.getInstance().initComponent()方法已經被標記為@Deprecated了,但是ribbon的DiscoveryEnabledNIWSServerList組件代碼中依然是通過DiscoveryManager來獲取EurekaClient對象的。

獲取服務地址

mport java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.client.ClientFactory;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.RoundRobinRule;
import com.netflix.loadbalancer.Server;

/**
 * @ClassName: AlanServiceAddressSelector
 * @Description:  獲取到目標服務注冊在Eureka地址
 * @author SuXun
 * @date 2018年3月2日 下午5:23:24
 */
public class AlanServiceAddressSelector {
	
	private static final Logger log = LoggerFactory.getLogger(AlanServiceAddressSelector.class);
	private static RoundRobinRule chooseRule = new RoundRobinRule();

	/**
	 * 根據輪詢策略選擇一個地址
	 * @param clientName ribbon.properties配置文件中配置項的前綴名, 如myclient
	 * @return null表示該服務當前沒有可用地址
	 */
	public static AlanServiceAddress selectOne(String clientName) {
		// ClientFactory.getNamedLoadBalancer會緩存結果, 所以不用擔心它每次都會向eureka發起查詢
		@SuppressWarnings("rawtypes")
		DynamicServerListLoadBalancer lb = (DynamicServerListLoadBalancer) ClientFactory
				.getNamedLoadBalancer(clientName);
		Server selected = chooseRule.choose(lb, null);
		if (null == selected) {
			log.warn("服務{}沒有可用地址", clientName);
			return null;
		}
		log.debug("服務{}選擇結果:{}", clientName, selected);
		return new AlanServiceAddress(selected.getPort(), selected.getHost());
	}

	/**
	 * 選出該服務所有可用地址
	 * @param clientName
	 * @return
	 */
	public static List<AlanServiceAddress> selectAvailableServers(String clientName) {
		@SuppressWarnings("rawtypes")
		DynamicServerListLoadBalancer lb = (DynamicServerListLoadBalancer) ClientFactory
				.getNamedLoadBalancer(clientName);
		List<Server> serverList = lb.getServerList(true);
		// List<Server> serverList = lb.getReachableServers();
		if (serverList.isEmpty()) {
			log.warn("服務{}沒有可用地址", clientName);
			return Collections.emptyList();
		}
		log.debug("服務{}所有選擇結果:{}", clientName, serverList);
		List<AlanServiceAddress> address = new ArrayList<AlanServiceAddress>();
		for (Server server : serverList) {
		address.add(new AlanServiceAddress(server.getPort(), server.getHost()));
		}
		return address;
	}
}

地址實體類:

/**
 * @ClassName: AlanServiceAddress
 * @Description:  地址實體類
 * @author SuXun
 * @date 2018年3月2日 下午2:14:17
 */
public class AlanServiceAddress {
	private int port;
	private String host;

	public AlanServiceAddress() {
	}

	public AlanServiceAddress(int port, String host) {
		this.port = port;
		this.host = host;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	/**
	 * 將服務地址轉換為 http://主機名:端口/ 的格式
	 * @return
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder(15 + host.length());
		sb.append("http://").append(host).append(":").append(port).append("/");

		return sb.toString();
	}
}

使用方法

// 選擇出myclient對應服務全部可用地址
List<AlanServiceAddress> list = AlanServiceAddressSelector.selectAvailableServers("myclient");
System.out.println(list);

// 選擇出myclient對應服務的一個可用地址(輪詢), 返回null表示服務當前沒有可用地址
AlanServiceAddress addr = AlanServiceAddressSelector.selectOne("myclient");
System.out.println(addr);

構建Feign客戶端

根據服務名在Eureka獲取地址,構建Feign,如果緩存有則返回緩存里的Feign,避免重復構建Feign。

構建Feign方法:

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommand.Setter;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.xxx.platform.common.netflex.eureka.AlanServiceAddress;
import com.xxx.platform.common.netflex.eureka.AlanServiceAddressSelector;
import feign.Feign;
import feign.Logger;
import feign.Request.Options;
import feign.Retryer;
import feign.Target;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.hystrix.HystrixFeign;
import feign.hystrix.SetterFactory;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.slf4j.Slf4jLogger;

/**
 * @ClassName: BaseFeignBuilder
 * @Description: 用於構建Feign
 * @author SuXun
 * @date 2018年3月5日 下午3:50:18
 */
public class BaseFeignBuilder {

	private static final org.slf4j.Logger log = LoggerFactory.getLogger(BaseFeignBuilder.class);
	private static ConcurrentHashMap<String, Object> cacheFeignMap = new ConcurrentHashMap<String, Object>();
	private static ConcurrentHashMap<String, String> cacheAddressMap = new ConcurrentHashMap<String, String>();

	/**
	 * 構建HystrixFeign,具有Hystrix提供的熔斷和回退功能,JacksonEncoder、JacksonDecoder、Slf4jLogger、Logger.Level.FULL
	 * @param apiType 使用feign訪問的接口類,如MedBodyClient.class
	 * @param clientName 配置文件中的ribbon client名字
	 * @param fallback 回退類
	 * @param url 添加網址
	 * @return
	 */
	public static <T> T buildHystrixFeign(Class<T> apiType, T fallback, String url) {
		// 之前用GsonEncoder()和GsonDecoder()對Date類型支持不好,改成JacksonEncoder和JacksonDecoder,日期轉換正常
		return buildHystrixFeign(apiType, fallback, url, new JacksonEncoder(), new JacksonDecoder(),
				new Slf4jLogger(BaseFeignBuilder.class), Logger.Level.FULL);
	}

	/**
	 * 構建HystrixFeign,具有Hystrix提供的熔斷和回退功能
	 * @param apiType 使用feign訪問的接口類,如MedBodyClient.class
	 * @param clientName clientName 配置文件中的ribbon client名字
	 * @param fallback 回退類
	 * @param url 添加網址
	 * @param encoder 編碼器
	 * @param decoder 解碼器
	 * @param logger 日志對象
	 * @param logLevel 日志級別
	 * @return
	 */
	public static <T> T buildHystrixFeign(Class<T> apiType, T fallback, String url, Encoder encoder, Decoder decoder,
			Logger logger, Logger.Level logLevel) {
		return HystrixFeign.builder().encoder(encoder).decoder(decoder).logger(logger).logLevel(logLevel)
				//options添加Feign請求響應超時時間
				.options(new Options(60 * 1000, 60 * 1000)).retryer(Retryer.NEVER_RETRY)
				.setterFactory(new SetterFactory() {
					@Override
					public Setter create(Target<?> target, Method method) {
						//添加Hstrix請求響應超時時間
						return HystrixCommand.Setter
								.withGroupKey(HystrixCommandGroupKey.Factory.asKey(apiType.getClass().getSimpleName()))
								.andCommandPropertiesDefaults(
										HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(60 * 1000) // 超時配置
						);
					}
				}).target(apiType, url, fallback);
	}

	/**
	 * 獲取HystrixFeign。緩存有在緩存取,緩存沒有重新構建Feign
	 * @param apiType 使用feign訪問的接口類,如MedBodyClient.class
	 * @param clientName clientName 配置文件中的ribbon client名字
	 * @param separator 添加網址分割
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getCacheFeign(Class<T> apiType, String clientName, T fallback, String separator) {
		
		String resultAddress = getResultAddress(clientName);

		String cacheKey = apiType.getName() + "-" + clientName + "-" + fallback.getClass().getName() + "-"
				+ resultAddress + separator;
		Object cacheFeign = cacheFeignMap.get(cacheKey);
		if (cacheFeign == null) {
			T buildFeign = buildHystrixFeign(apiType, fallback, resultAddress + separator);
			cacheFeignMap.put(cacheKey, buildFeign);
			return buildFeign;
		} else {
			return (T) cacheFeign;
		}
	}
	/**
	 * 獲取服務地址,取不到最新地址在緩存取舊地址,有新地址則返回新地址並刷新緩存
	 * @param clientName
 	 * @return
 	 */
	public static String getResultAddress(String clientName) {
		String recentAddress = null;
		AlanServiceAddress alanServiceAddress = AlanServiceAddressSelector.selectOne(clientName);
		recentAddress = alanServiceAddress == null ? "" : alanServiceAddress.toString();
		String cacheAddress = cacheAddressMap.get(clientName);
		String resultAddress = "";

		if (StringUtils.isBlank(recentAddress)) {
			if (StringUtils.isBlank(cacheAddress)) {
				log.error("服務" + clientName + "無可用地址");
				throw new RuntimeException("服務" + clientName + "無可用地址");
			} else {
				resultAddress = cacheAddress;
			}
		} else {
			resultAddress = recentAddress;
			cacheAddressMap.put(clientName, recentAddress);
		}
		return resultAddress;
	}
}

通用Feign接口

利用Feign繼承特性,特殊需求接口只要繼承通用接口就可獲得訪問通用接口的能力.

import java.util.Map;
import com.xxx.commons.base.ResultJsonEntity;
import feign.Headers;
import feign.Param;
import feign.RequestLine;

/**
 * @ClassName: BaseFeignClient
 * @Description: Feign基類
 * @author SuXun
 * @date 2018年3月5日 下午1:25:59
 */
// @Herders里邊的鍵值對冒號后面必須有個空格!
@Headers({ "Content-Type: application/json", "Accept: application/json" })
public interface BaseFeignClient {

	@RequestLine("POST /select")
	ResultJsonEntity select(Object obj);

	@RequestLine("GET /selectAll")
	ResultJsonEntity selectAll();

	// 因為Example無法被序列化成json,所以參數為Map
	@RequestLine("POST /selectByExample")
	ResultJsonEntity selectByExample(Map<String, Object> map);

	@RequestLine("GET /selectByPrimaryKey/{key}")
	ResultJsonEntity selectByPrimaryKey(@Param("key") String key);

	@RequestLine("POST /insertSelective")
	ResultJsonEntity insertSelective(Object obj);

	@RequestLine("POST /updateByPrimaryKeySelective")
	ResultJsonEntity updateByPrimaryKeySelective(Object obj);

	/**
	 * @param map key包含:int pageNum,int rowNum,T record和查詢條件
	 * @return
	 */
	@RequestLine("POST /getPageExampleList")
	ResultJsonEntity getPageExampleList(Map<String, Object> map);

}

通用回退類

因為使用HystrixClient.build(),使得Feign擁有熔斷器、回退的功能。這里根據通用接口實現的回退類。
這里的ResultJsonEntity、ResultEnum、ResultJsonUtil用於返回平台無關的json數據。

import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.xxx.commons.base.ResultJsonEntity;
import com.xxx.commons.enums.ResultEnum;
import com.xxx.commons.util.ResultJsonUtil;

/**
 * @ClassName: BaseFeignClientFallback
 * @Description:  Feign基類的回退類
 * @author SuXun
 * @date 2018年3月5日 下午5:04:13
 */
public class BaseFeignClientFallback implements BaseFeignClient {

	protected static final Logger LOG = LoggerFactory.getLogger(BaseFeignClientFallback.class);

	@Override
	public ResultJsonEntity select(Object obj) {
		LOG.error("{} select 出錯 進入熔斷 ", this.getClass().getName());
		return ResultJsonUtil.returnResult(ResultEnum.FAIL);
	}

	@Override
	public ResultJsonEntity selectAll() {
		LOG.error("{} selectAll 出錯 進入熔斷", this.getClass().getName());
		return ResultJsonUtil.returnResult(ResultEnum.FAIL);
	}

	@Override
	public ResultJsonEntity selectByExample(Map<String, Object> map) {
		LOG.error("{} selectByMap 出錯 進入熔斷 ", this.getClass().getName());
		return ResultJsonUtil.returnResult(ResultEnum.FAIL);
	}

	@Override
	public ResultJsonEntity selectByPrimaryKey(String key) {
		LOG.error("{} selectByPrimaryKey 出錯 進入熔斷 ", this.getClass().getName());
		return ResultJsonUtil.returnResult(ResultEnum.FAIL);
	}

	@Override
	public ResultJsonEntity insertSelective(Object obj) {
		LOG.error("{} insertSelective 出錯 進入熔斷 ", this.getClass().getName());
		return ResultJsonUtil.returnResult(ResultEnum.FAIL);
	}

	@Override
	public ResultJsonEntity updateByPrimaryKeySelective(Object obj) {
		LOG.error("{} updateByPrimaryKeySelective 出錯 進入熔斷 ", this.getClass().getName());
		return ResultJsonUtil.returnResult(ResultEnum.FAIL);
	}

	@Override
	public ResultJsonEntity getPageExampleList(Map<String, Object> map) {
		LOG.error("{} getPageExampleList 出錯 進入熔斷 ", this.getClass().getName());
		return ResultJsonUtil.returnResult(ResultEnum.FAIL);
	}
}

FeignClient使用方法

為了使Feign擁有負載均衡的能力,需要在 @ModelAttribute 注解的方法重復調用 getCacheFeigngetCacheFeign 方法可以獲取最新的地址,根據地址構建Feign或者在緩存取出Feign。

private BaseFeignClient xxxClient = null;
@ModelAttribute
public xxxEntity get(@RequestParam(required = false) String id) {
	xxxClient = BaseFeignBuilder.getCacheFeign(BaseFeignClient.class,
			"xxx-service", new BaseFeignClientFallback(), "xxx");
}

遺憾

非常遺憾,這里是手動獲取Eureka中的地址,看起來還不夠優雅。
Feign可以結合Ribbon使用,通過傳入服務名找地址,之前實現后發現需要兩次訪問才可以正確訪問到服務,兩次中必然有一次返回找不到地址,所以沒有使用。我會繼續研究,下次更新使Feign進行Http調用更優雅一些。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM