背景
当本地服务和服务器公用同一个 nacos 时(开发环境),亦或者需要连接测试环境进行问题排查时,我们可能会将本地服务注册到服务器的命名空间,这样会导致服务器的请求会被打到本地。而如果本地代码和服务器的代码不一致,就可能会导致客户端异常问题。
而如果我们只是简单的把自己的服务注册到自己的命名空间,和服务器命名空间隔离,却又会导致本地服务请求不到服务器命名空间内注册的其他服务。
网上能找到相关问题的解决方案,比如这篇文章 Nacos服务跨分组调用 就是通过分组隔离,然后重写服务发现策略来进行跨分组服务调用从而解决上述问题的。
本文会涉及到如何加载自己的配置文件,并且让其优先级高于 bootstrap.yml 等配置文件(也会高于 nacos配置中心 的配置 )
本文主要讲的只是一种新的解决思路,至少是我没在网上找到的思路。并不是更好。
相关依赖
springboot:2.7.15
隔离方法
命名空间隔离
首先,我们需要把我们自己本地的服务注册到自己的命名空间中,这个没什么好说的
请求服务器其他服务的方法
通过 FeignClient 注解中的 url 读取配置文件的属性,如下所示
@FeignClient(value = "${server.application1.server-name}", url = "${server.application1.url:}")
public interface ITestFacade {
}
仅当配置文件中存在该属性时,才会请求该属性对应 url 的服务,而属性不存在时,则走项目原来的逻辑
加载自定义配置文件
需要考虑与 spring 原生的配置方式区分开,降低耦合度。且能避免和正式的配置混在一起。具体如下
配置文件路径的配置方式
考虑通过命令行的方式传入,可以在 idea 的启动配置中进行如下配置,当没有进行配置时,就会走项目原本的调用逻辑。而该配置只需要在本地环境配置,因此服务器启动的命令不特意加入该配置,就不会对服务器环境产生任何影响。
a.yml 的配置如下所示
#feign 请求的服务和ip
server:
application1:
server-name: application1
#通过feign请求时需要请求的服务的 url 地址,不配置则feign会走项目原本的逻辑
url: http://192.168.1.1:8080
spring:
cloud:
nacos:
discovery:
#配置自己私有的命名空间
namespace: 8cas0d6r-dax7-467a-9f99-6ed7fcd9f3c8
配置加载方式
由于需要配置注册到自己命名空间,且优先级需要高于 bootstrap.yml ,否则配置就无法生效,所以就需要考虑让自己的配置优先级高于 bootstrap.yml ,具体有两种方案
方案1:
通过自定义 EnvironmentPostProcessor ,从而在 spring 环境准备好后,加载自己指定的配置文件,并且将其优先级配置的比 bootstrap.yml 高。具体代码如下所示
public class CustomConfigEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
try {
//从java 启动命令行中获取配置文件路径
String fileName = environment.getProperty("customConfig.path");
Resource resource = null;
//优先从项目的classPath读取配置文件(打包前在项目的 resources 文件夹里,打包后读取jar包中的文件)
resource = new ClassPathResource(fileName);
if (!resource.exists()) {
//绝对路径,用绝对路径就可以让多个服务公用一个配置文件(如果时相对路径,打包前是项目最外层pom.xml同目录下的文件,打包后就是jar所在目录)
resource = new FileUrlResource(fileName);
if (!resource.exists()) {
//当配置不存在时,则直接跳过,配置不生效,就会和没进行任何隔离的效果一样
return;
}
}
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
List<PropertySource<?>> config = loader.load("custom config", resource);
for (PropertySource<?> propertySource : config) {
//addFirst 让优先级最高
// environment.getPropertySources().addFirst(propertySource);
//让优先级比 命令行的args 低一级,但会比bootstrap.yml高和nacos配置中心的配置高
environment.getPropertySources().addAfter("commandLineArgs", propertySource);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
然后需要在 resources/META-INF 下配置 spring.factories
spring.factories 的配置如下所示
org.springframework.boot.env.EnvironmentPostProcessor=\\
com.zzz.CustomConfigEnvironmentPostProcessor
(当然也可以通过监听 ApplicationEnvironmentPreparedEventEvent 或者 SringApplication.prepareContext 环节中的某个环境的扩展点写上面类似的代码实现)
如果想了解spring 配置的加载流程可以看看这篇文章 nacos bootstrap.yml 和 spring.config.import 加载配置的流程区别
方案2
利用命令行方式(main 方法的 args )加载自定义配置,具体代码如下所示
public class TestApplication {
public static void main(String[] args) {
try {
//从java 启动命令行中获取配置文件路径
SimpleCommandLinePropertySource simpleCommandLinePropertySource = new SimpleCommandLinePropertySource(args);
String fileName = simpleCommandLinePropertySource.getProperty("customConfig.path");
Resource resource = null;
//优先从项目的classPath读取配置文件(打包前在项目的 resources 文件夹里,打包后读取jar包中的文件)
resource = new ClassPathResource(fileName);
if (!resource.exists()) {
//绝对路径,用绝对路径就可以让多个服务公用一个配置文件(如果时相对路径,打包前是项目最外层pom.xml同目录下的文件,打包后就是jar所在目录)
resource = new FileUrlResource(fileName);
if (!resource.exists()) {
//当配置不存在时,则直接跳过,配置不生效,就会和没进行任何隔离的效果一样
return;
}
}
YamlPropertiesFactoryBean yamlBean = new YamlPropertiesFactoryBean();
yamlBean.setResources(resource);
Properties properties = yamlBean.getObject();
if (properties.size() > 0) {
List<String> config = new ArrayList<String>();
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
if (simpleCommandLinePropertySource.containsProperty(entry.getKey().toString())) {
//如果命令行中本身就存在参数,就跳过,防止自定义配置文件的配置优先级比命令行高
continue;
}
config.add("–" + entry.getKey() + "=" + entry.getValue());
}
//args 比bootstrap.yml高和nacos配置中心的配置高
args = config.toArray(new String[config.size()]);
}
} catch (IOException e) {
e.printStackTrace();
}
SpringApplication.run(TestApplication.class, args);
}
}
命令行的配置的优先级本身就比 bootstrap.yml 和 nacos 高。所以不需要额外进行处理
和背景中给出文章的方案对比
本文的方法其实并不会更好,可能就是配置起来更简单,耦合度更低,简单。
而背景中的方案是是通过自定义发现策略实现的,更加灵活,可以自定义自己的规则进行不同的负载,比如方案中是可以跨分组请求,但同时也能改成跨命名空间请求,还可以改成根据前端请求头中的参数进行负载。
评论前必须登录!
注册