前言
之前業(yè)務(wù)部門(mén)的某項(xiàng)目微服務(wù)調(diào)用關(guān)系如下圖
后因業(yè)務(wù)改造需要,該項(xiàng)目需要將服務(wù)A部署到另外一個(gè)集群,但服務(wù)A仍然需要能調(diào)用到服務(wù)B,調(diào)用關(guān)系如下圖
之前調(diào)用方式是負(fù)責(zé)服務(wù)B的開(kāi)發(fā)團(tuán)隊(duì)提供相應(yīng)的feign客戶端包給到服務(wù)A開(kāi)發(fā)團(tuán)隊(duì),服務(wù)A開(kāi)發(fā)團(tuán)隊(duì)直接將客戶端包引入到項(xiàng)目,在通過(guò)@EnableFeignClients來(lái)激活feign調(diào)用,現(xiàn)在跨了不同集群,而且2個(gè)集群間的注冊(cè)中心也不一樣,之前的調(diào)用方式就不大適用了。
業(yè)務(wù)部門(mén)的技術(shù)負(fù)責(zé)人就找到我們部門(mén),看我們有沒(méi)有什么方案。當(dāng)時(shí)我們提供的方案,一種是服務(wù)A團(tuán)隊(duì)自己開(kāi)發(fā)客戶端接口去調(diào)用服務(wù)B,但這個(gè)方案工作量比較大。另外一種方案,就是通過(guò)改造openfeign。在業(yè)內(nèi)一直很流行一句話,沒(méi)有什么是加一層解決不了的
破局
后面我們提供的方案如下圖
本質(zhì)上就是原來(lái)服務(wù)A直接調(diào)用服務(wù)B,現(xiàn)在是服務(wù)A先通過(guò)和服務(wù)B同集群的網(wǎng)關(guān),間接調(diào)用服務(wù)B。思路已經(jīng)有了,但是我們需要實(shí)現(xiàn)業(yè)務(wù)能夠少改代碼,就能實(shí)現(xiàn)該需求
實(shí)現(xiàn)思路
通過(guò)feign的url + gateway開(kāi)啟基于服務(wù)注冊(cè)中心自動(dòng)服務(wù)路由功能
改造步驟
1、自定義注解EnableLybGeekFeignClients
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(LybGeekFeignClientsRegistrar.class)
public @interface EnableLybGeekFeignClients {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
* {@code @ComponentScan(basePackages="org.my.pkg")}.
* @return the array of 'basePackages'.
*/
String[] value() default {};
/**
* Base packages to scan for annotated components.
* <p>
* {@link #value()} is an alias for (and mutually exclusive with) this attribute.
* <p>
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names.
* @return the array of 'basePackages'.
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return the array of 'basePackageClasses'.
*/
Class<?>[] basePackageClasses() default {};
/**
* A custom <code>@Configuration</code> for all feign clients. Can contain override
* <code>@Bean</code> definition for the pieces that make up the client, for instance
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
*
* @return list of default configurations
*/
Class<?>[] defaultConfiguration() default {};
/**
* List of classes annotated with @FeignClient. If not empty, disables classpath
* scanning.
* @return list of FeignClient classes
*/
Class<?>[] clients() default {};
}
其實(shí)是照搬EnableFeignClients,差別只是import的bean不一樣
2、擴(kuò)展原生的FeignClientsRegistrar
擴(kuò)展的核心內(nèi)容如下
@SneakyThrows
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
Class feignClientFactoryBeanClz = ClassUtils.forName("org.springframework.cloud.openfeign.FeignClientFactoryBean",Thread.currentThread().getContextClassLoader());
String name = getName(attributes);
String customUrl = getCustomUrl(getUrl(attributes),name);
。。。省略其他代碼
}
private String getCustomUrl(String url,String serviceName){
if(StringUtils.hasText(url)){
return url;
}
String gateWay = environment.getProperty("lybgeek.gateWayUrl");
if(StringUtils.isEmpty(gateWay)){
return url;
}
if(serviceName.startsWith("http://")){
serviceName = StrUtil.trim(serviceName.replace("http://",""));
}
String customUrl = URLUtil.normalize(gateWay + "/" + serviceName);
log.info("feign customed with new url:【{}】",customUrl);
return customUrl;
}
3、gateway開(kāi)啟基于服務(wù)注冊(cè)中心自動(dòng)服務(wù)路由功能
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
測(cè)試
測(cè)試提供一個(gè)消費(fèi)者、服務(wù)提供者、網(wǎng)關(guān)、注冊(cè)中心
在消費(fèi)者的啟動(dòng)類(lèi)去掉原生的EnableFeignClients注解,采用我們自定義注解EnableLybGeekFeignClients
@SpringBootApplication
@EnableLybGeekFeignClients(basePackages = "com.github.lybgeek")
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class);
}
}
消費(fèi)者application.yml開(kāi)啟feign調(diào)用日志
logging:
level:
# feign調(diào)用所在的包
com.github.lybgeek.api.feign: debug
feign:
client:
config:
default:
# 開(kāi)啟feign記錄請(qǐng)求和響應(yīng)的標(biāo)題、正文和元數(shù)據(jù)
loggerLevel: FULL
通過(guò)消費(fèi)端調(diào)用服務(wù)提供者
可以正常訪問(wèn),我們觀察消費(fèi)者控制臺(tái)輸出的信息
我們可以發(fā)現(xiàn),此次調(diào)用,是服務(wù)與服務(wù)之間的調(diào)用,說(shuō)明我們擴(kuò)展的feign保留了原本feign的能力
我們對(duì)消費(fèi)者的application.yml,新增如下內(nèi)容
lybgeek:
gateWayUrl: localhost:8000
再通過(guò)消費(fèi)端調(diào)用服務(wù)提供者
可以正常訪問(wèn),我們觀察消費(fèi)者控制臺(tái)輸出的信息
同時(shí)觀察網(wǎng)關(guān)控制臺(tái)輸出的信息
我們可以發(fā)現(xiàn),此次調(diào)用,是通過(guò)網(wǎng)關(guān)路由到服務(wù)再產(chǎn)生調(diào)用,說(shuō)明我們擴(kuò)展的feign已經(jīng)具備通過(guò)網(wǎng)關(guān)請(qǐng)求服務(wù)的能力
總結(jié)
可能有朋友會(huì)說(shuō),何必這么麻煩擴(kuò)展,直接通過(guò)
@FeignClient(name = "${feign.instance.svc:provider}",url="${lybgeek.gateWayUrl: }/${feign.instance.svc:provider}",path = InstanceServiceFeign.PATH,contextId = "instance")
不也可以實(shí)現(xiàn)。其實(shí)如果帶入當(dāng)時(shí)的業(yè)務(wù)場(chǎng)景考慮,就會(huì)發(fā)現(xiàn)這種方式,需要改的地方比直接擴(kuò)展feign多得多,而且一旦出問(wèn)題,不好集中回滾。有時(shí)候脫離業(yè)務(wù)場(chǎng)景,去談?wù)摷夹g(shù)實(shí)現(xiàn),會(huì)容易走偏文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-535094.html
demo鏈接
https://github.com/lyb-geek/springboot-cloud-metadata-ext文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-535094.html
到了這里,關(guān)于聊聊不同集群的微服務(wù)如何通過(guò)feign調(diào)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!