今天给大家分享一个实用技能,dubbo随心注册,让你的微服务根据实际需要来决定是否发布到注册中心。
我是一个热衷于编程和解决问题的'资深'码农,目前正在开发一个venus的商城项目,在这个过程中我会把各种有趣有料的技术技能记录下来(vue前端/dubbo微服务/可能还有大数据哦),感兴趣的朋友欢迎关注和交流。
本文使用的技术和版本如下:
dubbo 2.7.8spring-boot 2.2.10
问题的由来
为什么会有启动服务却不想注册到注册中心去的需求?如果你正在做微服务的项目,只要不是只有你一个人做开发,那么你就会碰到这种情况:我正在开发或者修改的服务还未完成,我本地启动服务是为了自己验证,自己启动一个本地的消费者,直接调用自己的服务提供者,因为服务没有完成,如果我把它注册到注册中心,那就会有别的消费者的请求发到我这里,我正在做的修改就会对其他人产生影响。
为了保证我在做的事情不影响其他人,我难道就不能启动本地的服务了?这肯定不行,所以就有了这么一个问题,我想启动服务的时候,自己能够根据情况决定要不要注册到nacos或者zookeeper等注册中心上去,这个问题有两个方法:
1、我本地启动的时候,将dubbo的组修改成一个单独的组,这样我就算注册上去,别人和我不在一个组里,他的请求也不会到我这里来。
这种方法可行,但是麻烦,需要在本地修改配置文件,而且要提交的时候,不能把你本地的配置提交上去,但是谁能保证你每次提交前都会记得把配置还原呢??
2、本地启动的时候,我服务就不注册过去了,自己要测试的话,在消费者项目中,指定调用本地localhost的服务(dubbo是可以绕过注册中心的,消费者和服务提供者直联)
这种方式就是本文介绍的内容。
在dubbo中该如何设置?
dubbo服务启动的时候如果不需要注册,那么在配置文件中添加下面一行是最简单的方法:
dubbo.registry.register=false
但是真正的需求并非这么简单,配置文件中这么改了,就像上面第一种方法,又不能推送到远程,不是一劳永逸的解决之道。
这就要另想办法了,我的思路是,我们要根据本地操作系统环境来决定是否注册,windows和mac一般是开发用的,那就不注册,如果是linux,一般是部署测试环境的,这种就正常注册,有两种方式可以尝试:
1、要么介入修改dubbo的初始化过程,在dubbo启动时,判断环境并修改Registry相关的类。
2、要么就介入spring-boot的初始化过程,在dubbo初始化之前,我往环境中设置dubbo.registry.register的值。
第一种方式我翻了官方文档没有找到可行的方法(也可能是我技艺不精,手动狗头:) )。最终只能锁定第二种方法了,我首先想到的就是分布式项目可能会使用的配置中心,类似于apollo和nacos这种,这种框架也是在spring启动时,从远程获取一些配置,然后项目根据这些配置初始化,项目运行过程中,再监听远程的配置变化,实现灰度发布等等功能,跟我第二中方法的思路一致。
配置中心的切入点,就是spring的ApplicationContextInitializer了。
spring的ApplicationContextInitializer的作用
这个接口其实定义了一种行为,可以让我们在spring项目初始化的过程中,插入我们自己希望执行的代码,我们只需要定义一个类实现这个接口,就像下面这样:
import org.springframework.context.ApplicationContextInitializer;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.core.annotation.Order;import org.springframework.core.env.ConfigurableEnvironment;import org.springframework.core.env.PropertiesPropertySource;import org.springframework.core.env.StandardEnvironment;import java.util.Properties;@Order(1) //这个顺序决定spring初始化的顺序,越小越优先执行@Log4j2public class DubboConfiguration implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext applicationContext) { if(VenusEnvironmentUtils.isDevelopEnv()) { ConfigurableEnvironment environment = new StandardEnvironment(); Properties properties = new Properties(); properties.put("dubbo.registry.register", false); PropertiesPropertySource propertySource = new PropertiesPropertySource("dubbo.debug", properties); environment.getPropertySources().addFirst(propertySource); applicationContext.setEnvironment(environment); log.info("develop environment, set dubbo.registry.register=false"); } }}
将自己实现的ApplicationContextInitializer添加到spring的启动序列中
但是上面我们定义的类,并不会被spring自动发现(使用@Component注解也不行),我们就需要手动将其加入到spring的初始化中了,那我们要从SpringApplication中找答案了。
spring-boot的启动方法可以改成下面这样:
public static void main( String[] args) { //ConfigurableApplicationContext application = SpringApplication.run(UserApplication.class, args); SpringApplication application = new SpringApplication(UserApplication.class); application.addInitializers(new DubboConfiguration()); application.run(args);}
我们的自定义的ApplicationContextInitializer就完美加入其中了。
根据环境来决定是否向注册中心注册
最后看一下我们的判断方法,isDevelopEnv()
public class VenusEnvironmentUtils { private static String system = System.getProperty("os.name").toLowerCase(); public static boolean isWindows(){ return system.contains("windows"); } public static boolean isMac(){ return system.contains("mac") && system.indexOf("os")>0 && system.indexOf("x")>0; } /** * 本地开发环境,识别windows操作系统和mac操作系统 * @return */ public static boolean isDevelopEnv(){ return isMac() || isWindows(); }}
这样就完美实现了目标,这个服务在部署到测试环境或者生产环境中时,服务正常注册,在我们本地启动的时候,就不向注册中心注册了,只用于我们本地直接调用,对别人和测试环境都不会有影响。