zoukankan      html  css  js  c++  java
  • 微服务之路(四)spring cloud config client

    前言

    可能大家用过或者了解过Spring Cloud,本章将简单的介绍一下Spring Cloud。

    主要议题

    • Spring Cloud技术体系
    • Spring/Spring Boot事件机制
    • Bootstrap配置属性
    • 理解Environment端点

    主体内容

    一、Spring Cloud技术体系

    思维导图

    二、Spring/Spring Boot事件机制

    • 设计模式
      • 观察者模式
      • 事件/监听器模式
    • Spring核心接口
      • ApplicationEvent
      • ApplicationListener

    1.观察者模式

    java.util.Observable是一个发布者

    java.util.Observer是订阅者

    发布者和订阅者可以是1:N的关系也可以是N:M的关系。这里抛出一个小的示例,具体了解可以参考其余资料:

    import java.util.Observable;
    import java.util.Observer;
    
    /**
     * @ClassName ObserverDemo
     * @Description 观察者模式
     * @Author 66477
     * @Date 2020/5/16 14:20
     * @Version v1.0
     **/
    public class ObserverDemo {
        public static void main(String[] args) {
    
            MyObservable myObservable= new MyObservable();
            //增加订阅者(观察者)
            myObservable.addObserver(new Observer() {
                @Override
                public void update(Observable o, Object arg) {
                    System.out.println(arg);
                }
            });
            
            //为什么把数据发给观察者之前要先调用setChanged()?
            //因为数据有时更新得太快,如果想要数据在某一间隔才发给观察者,例如没5秒采集数据一次。逻辑上可以5秒后调用setChanged()一次,才能向观察者发送数据。
            //所以setChanged()用于控制数据的发送时机
            myObservable.setChanged();//说明在这里它已经变化了
            myObservable.notifyObservers("Hello World");//通知订阅者
        }
        
         private static void echoIterable(){
            List<Integer> values = Arrays.asList(1,2,3,4,5);
            Iterator<Integer> integerIterator = ((List) values).iterator();
            while(integerIterator.hasNext()){//通过循环,主动获取(拉模式)
                integerIterator.next();
            }
        }
        
        //自己写一个类继承Observable,因为Observable类中的setChanged方法是protected
        public static class MyObservable extends  Observable{
    
            public  void setChanged(){
                super.setChanged();
            }
        }
    }
    

    2.事件/监听模式

    java.util.EventObject:事件对象,事件对象总是关联着事件源(source)

    java.uitl.EventListener:事件监听接口(标记)

    3.Spring事件/监听

    • Spring核心接口
      • ApplicationEvent
      • ApplicationListener
    /**
     * @ClassName SpringEventListenerDemo
     * @Description 自定义 事件/监听器
     * @Author 66477
     * @Date 2020/5/16 21:57
     * @Version v1.0
     **/
    public class SpringEventListenerDemo {
        public static void main(String[] args) {
            //Annotation 驱动的Spring上下文
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
            //1.注册监听器
            context.addApplicationListener(new ApplicationListener<MyApplicationEvent>() {
                /**
                 * 3.监听器得到事件
                 * @param event
                 */
                @Override
                public void onApplicationEvent(MyApplicationEvent event) {
                    //打印事件源和事件上下文对象
                    System.out.println("接收到事件:"+event.getSource()+"@"+event.getApplicationContext());
                }
            });
            context.refresh();
            //2.发布事件
            context.publishEvent(new MyApplicationEvent(context,"Hello World"));
            context.publishEvent(new MyApplicationEvent(context,1));
            context.publishEvent(new MyApplicationEvent(context,new Integer(100)));
        }
    
        private static class MyApplicationEvent extends ApplicationEvent{
    
            private final ApplicationContext applicationContext;
            //添加构造方法参数:上下文对象参数
            public MyApplicationEvent(ApplicationContext applicationContext, Object source) {
                super(source);
                this.applicationContext=applicationContext;
            }
            //设置上下文对象getter方法
            public ApplicationContext getApplicationContext() {
                return applicationContext;
            }
        }
    
    }
    

    其中补充一点:ConfigFileApplicationListener类是管理配置文件的,如:application.properties,以及application.yaml。

    它有一个模式:application-{profile}.properties

    当profile=dev、test时,

    它可能会有加载优先级的顺序,当然,这也不是绝对的,比如:

    1.application-{profile}.properties

    2.application.properties

    Java SPI:ServiceLoader

    配置项位置:Spring Boot在相对于ClassPath:/META-INF/spring.factories

    Spring SPI:

    # Application Listeners
    org.springframework.context.ApplicationListener=
    org.springframework.boot.ClearCachesApplicationListener,
    org.springframework.boot.builder.ParentContextCloserApplicationListener,
    org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
    org.springframework.boot.context.FileEncodingApplicationListener,
    org.springframework.boot.context.config.AnsiOutputApplicationListener,
    org.springframework.boot.context.config.ConfigFileApplicationListener,
    org.springframework.boot.context.config.DelegatingApplicationListener,
    org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,
    org.springframework.boot.context.logging.LoggingApplicationListener,
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
    

    那么它是如何控制顺序?

    实现Ordered类以及标记@Order,Ordered里面有个getOrder方法。

    在Spring里面,数值越小越优先。

    4.Spring/SpringBoot事件机制

    • SpringBoot核心事件,事件是用来通知监听事件的监听器某件事情的处理状态,在SpringBoot启动过程中,除了Spring框架的事件外,SpringBoot还会触发其他的一些事件,这些事件按下列顺序触发:

      (1)ApplicationStartingEvent:项目刚启动时触发,此时除了注册监听器和初始器之外,其他所有处理都没有开始;
      (2)ApplicationEnvironmentPreparedEvent:上下文得到环境信息之后触发,此时上下文创建还没有创建;
      (3)ApplicationPreparedEvent:bean的定义信息加载完成之后触发,此时bean还没有初始化;
      (4)ApplicationReadyEvent:在所有bean初始化完毕,所有回调处理完成,系统准备处理服务请求时触发;
      (5)ApplicationFailedEvent:启动过程出现异常时候触发。

    知道SpringBoot启动时发送的时间后,我们就能自定义监听器对这些事件进行监听,从而达到扩展启动流程的目的。我们的自定义监听器需要实现ApplicationListener接口,同时指定需要监听哪个事件。

    然后在main方法中,将自定义监听器加入到SpringApplication中。

    除了上述代码的方式添加监听器外,还可以通过在classpath下创建META-INF/spring.factories文件,并将自定义监听器通过键值对的形式(org.springframework.context.ApplicationListener = com.example.MyListener)加入到SpringApplication中。

    5.Spring Cloud 事件/监听器

    配置项位置:Spring Cloud在相对于ClassPath:/META-INF/spring.factories,找到BootstrapApplicationListener类,在IDEA中find usages。内容如下:

    # Application Listeners
    org.springframework.context.ApplicationListener=
    org.springframework.cloud.bootstrap.BootstrapApplicationListener,
    org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,
    org.springframework.cloud.context.restart.RestartListener
    

    BootstrapApplicationListener:它加载的优先级高于ConfigFileApplicationListener,所以application.properties文件即使定义也配置不到!

    原因在于:

    BootstrapApplicationListener第六优先

    ConfigFileApplicationListener第十一优先

    如本体现?

    BootstrapApplicationListener类实现了Ordered接口。我这个版本的springboot列出了Ordered的最高优先级和最低优先级:

     int HIGHEST_PRECEDENCE = -2147483648;
     int LOWEST_PRECEDENCE = 2147483647;
    

    然后BootstrapApplicationListener类定义的默认优先级为-2147483643,区块代码如下:

     ...
     public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";
     public static final int DEFAULT_ORDER = -2147483643;
     public static final String DEFAULT_PROPERTIES = "springCloudDefaultProperties";
     private int order = -2147483643;
     ...
    

    我们还知道Spring中数字越小,优先级越高,而这里又是负值,则可以说BootstrapApplicationListener优先级距离最高优先级5个数字,那它本身就是第六优先级,同理,看下ConfigFileApplicationListener类,它的默认优先级为 -2147483638,距离最高优先级中间差了10个数字,则可以说ConfigFileApplicationListener为第十一优先级。

    private static final String DEFAULT_PROPERTIES = "defaultProperties";
    private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/";
    private static final String DEFAULT_NAMES = "application";
    private static final Set<String> NO_SEARCH_NAMES = Collections.singleton((Object)null);
    private static final Bindable<String[]> STRING_ARRAY = Bindable.of(String[].class);
    private static final Bindable<List<String>> STRING_LIST = Bindable.listOf(String.class);
    private static final Set<String> LOAD_FILTERED_PROPERTY;
    public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
    public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
    public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
    public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
    public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
    public static final int DEFAULT_ORDER = -2147483638;
    private final DeferredLog logger = new DeferredLog();
    private static final Resource[] EMPTY_RESOURCES;
    private static final Comparator<File> FILE_COMPARATOR;
    private String searchLocations;
    private String names;
    private int order = -2147483638;
    

    BootstrapApplicationListener类

    1.负责加载bootstrap.properties或者bootstrap.yaml。

    2.负责初始化Bootstrap ApplicationContext ID="bootstrap"

    ConfigurableApplicationContext context = builder.run();
    

    Bootstrap是一个跟Spring上下文,parent=null

    联想ClassLoader

    ExtClassLoader<-AppClassLoader<-

    System ClassLoader->Bootstrap ClassLoader

    ConfigurableApplicationContext

    标准实现类:AnnotationConfigApplicationContext

    三、Bootstrap配置属性

    • Bootstrap配置文件路径

      -spring.cloud.bootstrap.location

    • 覆盖远程配置属性

      -spring.cloud.config.allowOverride

    • 自定义Bootstrap配置

      -@BootstrapConfiguration

    • 自定义Bootstrap配置属性源

      -PropertySourceLocator

    1.自定义Bootstrap配置属性源

    (1)实现PropertySourceLocator

    (2)暴露该实现作为一个Spring Bean

    (3)实现PropertySource

    (4)

    @Configuration
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public static class MyPropertySourceLoator implements PropertySourceLocator{
    
    
       @Override
       public PropertySource<?> locate(Environment environment) {
          Map<String,Object> source = new HashMap<>();
          source.put("server.port","9090");
          MapPropertySource propertySource = new MapPropertySource("my-property-souce",source);
          return propertySource;
       }
    }
    

    四、理解Environment端点

    • Spring Boot Actuator
      • endpoint:"/env"
    • Spring Framework
      • Environment API

    1.默认只开放info, health这两个端口,要想开放其他端口,配置如下:

    management.endpoints.web.exposure.include=beans,info
    

    关于Env的端点,可以看看EnvironmentEndpoint这个类。

    Environment关联多个带名称的PropertySource,可以参考以下Spring Framework的源码AbstractApplicationContext#initPropertySources#子类AbstractRefreshableWebApplicationContext:

     protected void initPropertySources() {
            ConfigurableEnvironment env = this.getEnvironment();
            if (env instanceof ConfigurableWebEnvironment) {
                ((ConfigurableWebEnvironment)env).initPropertySources(this.servletContext, this.servletConfig);
            }
        }
    

    Environment有两种实现方式:

    普通类型:StandardEnvironment

    Web类型:StandardServletEnvironment

    Environment

    • AbstractEnvironment类
      • StardEnvironment

    Environment关联着一个PropertySources实例

    PropertySources关联着多个PropertySource,并且有优先级

    其中比较常用的PropertySource实现:

    Java System#getenv实现:名称“systemProperties”,对应的内容System.getProperties()

    Java System#nev实现:名称“systemEnvironment”,对应的内容System.getProperties()

    关于SpringBoot优先级顺序可以参考:https://docs.spring.io/spring-boot/docs/2.3.0.RELEASE/reference/htmlsingle/#boot-features-external-config

  • 相关阅读:
    Token_使用JWT生成token
    JSON Web Tokens(JWT)
    Hadoop 2.0 中的资源管理框架
    Hadoop 1.0 和 2.0 中的数据处理框架
    Hadoop 概述
    探索 OpenStack 之(17):计量模块 Ceilometer 中的数据收集机制
    探索 OpenStack 之(16):计量模块 Ceilometer 介绍及优化
    VMware + OpenStack: 从 Plugin 到 VIO (VMware Integrated OpenStack)的演进
    探索 OpenStack 之(15):oslo.messaging 和 Cinder 中 MessageQueue 消息的发送和接收
    探索 OpenStack 之(14):OpenStack 中 RabbitMQ 的使用
  • 原文地址:https://www.cnblogs.com/xusp/p/12952816.html
Copyright © 2011-2022 走看看