使用spring cloud有一段時(shí)間了兜看,相信很多使用spring cloud的時(shí)候都遇到過(guò)這樣的苦惱锥咸,項(xiàng)目分成了多個(gè)微服務(wù),有些業(yè)務(wù)需要互相依賴(lài)细移,第一就想到了使用feign調(diào)用搏予,但是我們?cè)陂_(kāi)發(fā)debug時(shí),需要在本機(jī)啟動(dòng)eureka server弧轧,還需要啟動(dòng)自己調(diào)試的方法依賴(lài)的其他微服務(wù)雪侥,一來(lái)二去,很可能調(diào)試方法依賴(lài)服務(wù)A精绎,服務(wù)A又依賴(lài)B速缨,我們就需要啟動(dòng)很多服務(wù),以及各個(gè)服務(wù)所依賴(lài)的環(huán)境代乃,導(dǎo)致調(diào)試非常的麻煩旬牲,接下來(lái)給大家介紹一種方法,可以不用啟動(dòng)這些環(huán)境搁吓,就可以調(diào)試問(wèn)題的方案原茅。
一、搭建測(cè)試環(huán)境
將項(xiàng)目中項(xiàng)目中所有服務(wù)搭建一個(gè)完整的環(huán)境擎浴,也可以直接使用測(cè)試環(huán)境员咽,但是環(huán)境中的服務(wù)必須是可以使用互聯(lián)網(wǎng)可以訪問(wèn)到毒涧,最好使用域名訪問(wèn)贮预。
二、使用本地環(huán)境調(diào)用測(cè)試環(huán)境服務(wù)
要想讓本地環(huán)境調(diào)用測(cè)試環(huán)境契讲,需要了解eureka server中的服務(wù)是如何被各個(gè)微服務(wù)調(diào)用的仿吞,feign調(diào)用微服務(wù)時(shí),使用了ribbon來(lái)做負(fù)載均衡捡偏,使用了Hystrix來(lái)做熔斷唤冈,feign中的服務(wù)信息,來(lái)自于EurekaClient.fetchRegistry()方法可以取到所有eureka server上的服務(wù)银伟,feign根據(jù)服務(wù)信息你虹,獲取到服務(wù)的ip(也可以是host,但是一般使用ip彤避,host還要配置機(jī)器host列表)和端口以及結(jié)合在feign聲明中的服務(wù)地址傅物,拼成http請(qǐng)求的完整地址,再發(fā)送http請(qǐng)求實(shí)現(xiàn)調(diào)用微服務(wù)的目的琉预。 那么關(guān)鍵就在于從Eureka中獲取到的服務(wù)列表信息中的ip地址董饰。以此提出以下兩種解決方案:
在測(cè)試環(huán)境中的服務(wù),直接在配置文件中,指定外網(wǎng)地址卒暂,這樣本地服務(wù)就可以用外網(wǎng)地址進(jìn)行服務(wù)訪問(wèn)了啄栓,但是這種方式,需要所有服務(wù)器開(kāi)啟相應(yīng)外網(wǎng)端口,存在安全隱患,而且所有的服務(wù)間的調(diào)用都通過(guò)外網(wǎng)訪問(wèn)恬偷,也會(huì)影響速度允粤,故不推薦。
下面介紹第二張情況五垮,通過(guò)修改FeignConfig來(lái)完成遠(yuǎn)程微服務(wù)的調(diào)用,對(duì)測(cè)試環(huán)境無(wú)侵入,只在客戶(hù)端設(shè)置崎场,即可完成對(duì)測(cè)試環(huán)境中服務(wù)的調(diào)用,上面例子介紹了feign調(diào)用微服務(wù)時(shí)遂蛀,需要拿到內(nèi)網(wǎng)ip和端口以及服務(wù)地址谭跨,拼成完整的服務(wù)地址,那我們可以在拼接完整的服務(wù)地址時(shí)李滴,將內(nèi)網(wǎng)ip直接更換為域名螃宙,然后通過(guò)域名調(diào)用到微服務(wù)(前面已經(jīng)說(shuō)到,測(cè)試環(huán)境的服務(wù)都要可以通過(guò)外網(wǎng)訪問(wèn)到)所坯,這樣就可以實(shí)現(xiàn)本地服務(wù)調(diào)用測(cè)試環(huán)境的服務(wù)谆扎,而不用在本地啟動(dòng)無(wú)數(shù)個(gè)依賴(lài)服務(wù)。為了不讓本地環(huán)境注冊(cè)到eureka上芹助,將本地服務(wù)關(guān)閉注冊(cè)到eureka堂湖,只設(shè)置從eureka拉取服務(wù),下邊給出配置例子:
eureka:
debug: true #自定義參數(shù)状土,開(kāi)始本地環(huán)境通過(guò)eurake調(diào)用test環(huán)境中的服務(wù)无蜂,
#設(shè)置為true開(kāi)啟,開(kāi)啟后將register-with-eureka配置參數(shù)設(shè)置為false,防止test環(huán)境調(diào)用到本地的微服務(wù)出錯(cuò)
debug-url: https://yuming.com #自定義參數(shù), 測(cè)試環(huán)境注冊(cè)微服務(wù)的真實(shí)訪問(wèn)路徑蒙谓,此配置適用于 同桌項(xiàng)目斥季,其他項(xiàng)目根據(jù)情況配置
client:
fetch-registry: true
register-with-eureka: false #本地使用時(shí),將此屬性設(shè)置為false累驮,不會(huì)將本地服務(wù)注冊(cè)到eurake上酣倾,但是本地服務(wù)可以從eurake上獲取到服務(wù)信息
service-url:
defaultZone: http://89.234.306.43:8761/eureka/
package com.anjl.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.*;
import feign.codec.ErrorDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.netflix.feign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.security.oauth2.client.feign.OAuth2FeignRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.stereotype.Component;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Map;
/**
* @author anjl
* 使用feign時(shí),使用此配置
*/
@Configuration
@EnableConfigurationProperties
@Slf4j
public class FeignConfig {
/**-----------以下配置是為了本地調(diào)試使用谤专,測(cè)試和正式環(huán)境將${eureka.debug}設(shè)置為false即可----------**/
@Value("${eureka.debug:false}")
private Boolean debug;
@Value("${eureka.debug-url:null}")
private String debugUrl;
public FeignConfig() {
}
@Bean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {
return new LoadFeignBalancerFeignClient(new DefaultClient(null, null), cachingFactory, clientFactory);
}
class LoadFeignBalancerFeignClient implements Client {
private LoadBalancerFeignClient loadBalancerFeignClient;
public LoadFeignBalancerFeignClient(Client delegate, CachingSpringLoadBalancerFactory lbClientFactory, SpringClientFactory clientFactory) {
loadBalancerFeignClient = new LoadBalancerFeignClient(delegate, lbClientFactory, clientFactory);
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
DefaultClient delegate = (DefaultClient) loadBalancerFeignClient.getDelegate();
delegate.setClientName(clientName);
return loadBalancerFeignClient.execute(request, options);
}
}
class DefaultClient extends Client.Default implements Client {
private String clientName;
public void setClientName(String clientName) {
this.clientName = clientName;
}
public DefaultClient(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) {
super(sslContextFactory, hostnameVerifier);
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
if(debug){
URI asUri = URI.create(request.url());
String url = String.format("%s/%s%s?%s", debugUrl, clientName, asUri.getPath(), asUri.getQuery());
request = Request.create(request.method(), url, request.headers(), request.body(), request.charset());
}
return super.execute(request, options);
}
}
/**--------------------end--------------------------**/
@Bean
Logger.Level feignLoggerLevel() {
//這里記錄所有躁锡,根據(jù)實(shí)際情況選擇合適的日志level
return Logger.Level.FULL;
}
}
package com.anjl.client;
import com.alibaba.fastjson.JSONObject;
import com.sunlands.zlcx.usercenter.config.OauthFeignConfig;
import com.sunlands.zlcx.usercenter.vo.feign.UsersGroupVO;
import com.sunlands.zlcx.usercenter.vo.response.BusinessResult;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient(value = "group-api", configuration = OauthFeignConfig.class)
public interface GroupInfoService {
/**
* 根據(jù)用戶(hù)id,查詢(xún)?nèi)航M信息
* @param userId userId
* @return Users
*/
@RequestMapping(value = "/group/simple")
List<Groups> getGroupInfoListByUserId(@RequestParam(value = "userId") Integer userId);
}