前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SpringCloud是如何动态更新配置的

SpringCloud是如何动态更新配置的

作者头像
tunsuy
发布于 2022-10-27 02:07:06
发布于 2022-10-27 02:07:06
2.6K00
代码可运行
举报
运行总次数:0
代码可运行

spring cloud在config配置管理的基础上,提供了consul config的配置管理和动态监听,那么这里面到底是怎样实现的,本文将为你揭秘。

注:这里讲的动态配置更新不只局限于consul,对于任意的配置都是这样的逻辑,本文将其spring源码进行详细的剖析。

前言

对于单体应用架构来说,会使用配置文件管理我们的配置,这就是之前项目中的application.properties或application.yml。

如果需要在多环境下使用,传统的做法是复制这些文件命名为application-xxx.properties,并且在启动时配置spring.profiles.active={profile}来指定环境。

微服务架构下我们可能会有很多的微服务,所以要求的不只是在各自微服务中进行配置,我们需要将所有的配置放在统一平台上进行操作,不同的环境进行不同的配置,运行期间动态调整参数等等。

于是Spring Cloud为我们提供了一个统一的配置管理,那就是Spring Cloud Config

spring cloud config简介

它为分布式系统外部配置提供了服务器端和客户端的支持,它包括config server端和 config client端两部分

  • Config server端是一个可以横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认 使用Git存储配置内容
  • Config client 是config server的客户端,用于操作存储在server中的配置属性

启动加载扩展点

spring boot提供在 META-INF/spring.factories 文件中增加配置,来实现一些程序中预定义的扩展点。

通过这种方式配置的扩展点好处是不局限于某一种接口的实现,而是同一类别的实现。

我们查看 spring-cloud-context 包中的 spring.factories 文件,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
org.springframework.cloud.context.restart.RestartListener
# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.cloud.util.random.CachedRandomPropertySourceAutoConfiguration

可以看到BootstrapConfiguration下面有一个item,PropertySourceBootstrapConfiguration,进入其代码,查看即继承关系,发现其实现了 ApplicationContextInitializer 接口,其目的就是在应用程序上下文初始化的时候做一些额外的操作。

在 Bootstrap 阶段,会通过 Spring Ioc 的整个生命周期来初始化所有通过key为org.springframework.cloud.bootstrap.BootstrapConfiguration 在 spring.factories 中配置的 Bean。

初始化的过程中,会获取所有 ApplicationContextInitializer 类型的 Bean,并设置回SpringApplication主流程当中。通过在 SpringApplication 的主流程中回调这些 ApplicationContextInitializer 的实例,做一些初始化的操作,即调用initialize方法。

下面我们就来看看PropertySourceBootstrapConfiguration这个方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
CompositePropertySource composite = new CompositePropertySource(
    BOOTSTRAP_PROPERTY_SOURCE_NAME);
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
boolean empty = true;
ConfigurableEnvironment environment = applicationContext.getEnvironment();
for (PropertySourceLocator locator : this.propertySourceLocators) {
  PropertySource<?> source = null;
  //回调所有实现PropertySourceLocator接口实例的locate方法,
  source = locator.locate(environment);
  if (source == null) {
    continue;
  }

  composite.addPropertySource(source);
  empty = false;
}
if (!empty) {
//从当前Enviroment中获取 propertySources
  MutablePropertySources propertySources = environment.getPropertySources();
  //省略...
  //将composite中的PropertySource添加到当前应用上下文的propertySources中
  insertPropertySources(propertySources, composite);
  //省略...
}

在这个方法中会回调所有实现 PropertySourceLocator 接口实例的locate方法, locate 方法返回一个 PropertySource 的实例,统一add到CompositePropertySource实例中。如果 composite 中有新加的PropertySource,最后将composite中的PropertySource添加到当前应用上下文的propertySources中。

SpringCloudConsul的配置加载

正如上面说的,在 Bootstrap 阶段,会通过 Spring Ioc 的整个生命周期来初始化所有通过key为org.springframework.cloud.bootstrap.BootstrapConfiguration 在 spring.factories 中配置的 Bean。同样的在spring.factories文件中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.consul.config.ConsulConfigAutoConfiguration
# Bootstrap Configuration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.consul.config.ConsulConfigBootstrapConfiguration

我们确实看到了又这样一个key存在,对应value为ConsulConfigBootstrapConfiguration类,我们看看该类的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration(proxyBeanMethods = false)
@ConditionalOnConsulEnabled
public class ConsulConfigBootstrapConfiguration {

  @Configuration(proxyBeanMethods = false)
  @EnableConfigurationProperties
  @Import(ConsulAutoConfiguration.class)
  @ConditionalOnProperty(name = "spring.cloud.consul.config.enabled",
      matchIfMissing = true)
  protected static class ConsulPropertySourceConfiguration {

    @Autowired
    private ConsulClient consul;

    @Bean
    @ConditionalOnMissingBean
    public ConsulConfigProperties consulConfigProperties() {
      return new ConsulConfigProperties();
    }

    @Bean
    public ConsulPropertySourceLocator consulPropertySourceLocator(
        ConsulConfigProperties consulConfigProperties) {
      return new ConsulPropertySourceLocator(this.consul, consulConfigProperties);
    }

  }

}

我们看到,这里只是注入了一些bean,我们注意下ConsulPropertySourceLocator这个类。

正如上面说的,SpringCloudConfig在启动的时候会回调所有实现 PropertySourceLocator 接口实例的locate方法,consul就是实现了PropertySourceLocator接口,具体类为ConsulPropertySourceLocator,实现了locate方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
@Retryable(interceptor = "consulRetryInterceptor")
public PropertySource<?> locate(Environment environment) {
  if (environment instanceof ConfigurableEnvironment) {
    ConfigurableEnvironment env = (ConfigurableEnvironment) environment;

    String appName = this.properties.getName();

    if (appName == null) {
      appName = env.getProperty("spring.application.name");
    }

    List<String> profiles = Arrays.asList(env.getActiveProfiles());

    String prefix = this.properties.getPrefix();

    List<String> suffixes = new ArrayList<>();
    // 不是文件类型的时候,后缀为 /,否则就是配置文件的后缀
    if (this.properties.getFormat() != FILES) {
      suffixes.add("/");
    } else {
      suffixes.add(".yml");
      suffixes.add(".yaml");
      suffixes.add(".properties");
    }

    // 路径
    String defaultContext = getContext(prefix, this.properties.getDefaultContext());

    for (String suffix : suffixes) {
      this.contexts.add(defaultContext + suffix);
    }
    // 追加环境及文件类型
    for (String suffix : suffixes) {
      addProfiles(this.contexts, defaultContext, profiles, suffix);
    }

    String baseContext = getContext(prefix, appName);

    // 应用名称前缀
    for (String suffix : suffixes) {
      this.contexts.add(baseContext + suffix);
    }
    for (String suffix : suffixes) {
      addProfiles(this.contexts, baseContext, profiles, suffix);
    }

    Collections.reverse(this.contexts);

    CompositePropertySource composite = new CompositePropertySource("consul");

    for (String propertySourceContext : this.contexts) {
      try {
        ConsulPropertySource propertySource = null;
        if (this.properties.getFormat() == FILES) {
          // 获取值
          Response<GetValue> response = this.consul.getKVValue(propertySourceContext, this.properties.getAclToken());
          // 添加当前索引
          addIndex(propertySourceContext, response.getConsulIndex());
          // 如果值不为空,则更新值并初始化
          if (response.getValue() != null) {
            ConsulFilesPropertySource filesPropertySource = new ConsulFilesPropertySource(propertySourceContext, this.consul, this.properties);
            // 解析配置内容
            filesPropertySource.init(response.getValue());
            propertySource = filesPropertySource;
          }
        } else {
          propertySource = create(propertySourceContext, this.contextIndex);
        }
        if (propertySource != null) {
          composite.addPropertySource(propertySource);
        }
      } catch (Exception e) {
        if (this.properties.isFailFast()) {
          log.error("Fail fast is set and there was an error reading configuration from consul.");
          ReflectionUtils.rethrowRuntimeException(e);
        } else {
          log.warn("Unable to load consul config from " + propertySourceContext, e);
        }
      }
    }

    return composite;
  }
  return null;
}

获取配置时,根据应用名称,路径,环境及配置类型拼接相应的路径,然后调用 Consul 获取 KV 值的接口,获取相应的配置,根据类型解析后放入环境中

配置动态刷新

感知到外部化配置的变更这部分代码的操作是需要用户来完成的。Spring Cloud Config 只提供了具备外部化配置可动态刷新的能力,并不具备自动感知外部化配置发生变更的能力。

比如如果你的配置是基于Mysql来实现的,那么在代码里面肯定要有能力感知到配置发生变化了,然后再显示的调用 ContextRefresher 的 refresh方法,从而完成外部化配置的动态刷新(只会刷新使用RefreshScope注解的Bean)。

下面我们来看看config框架是怎么进行动态刷新的?主要类是这个ContextRefresher,刷新方法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public synchronized Set refresh() {

    Map<String, Object> before = extract(
            this.context.getEnvironment().getPropertySources());
    //1、加载最新的值,并替换Envrioment中旧值
    addConfigFilesToEnvironment();
    Set<String> keys = changes(before,
            extract(this.context.getEnvironment().getPropertySources())).keySet();
    this.context.publishEvent(new EnvironmentChangeEvent(context, keys));
    //2、将refresh scope中的Bean 缓存失效: 清空
    this.scope.refreshAll();
    return keys;
}

上面ContextRefresher的refresh的方法主要做了两件事:

  • 1、触发PropertySourceLocator的locator方法,需要加载最新的值,并替换 Environment 中旧值
  • 2、Bean中的引用配置值需要重新注入一遍。重新注入的流程是在Bean初始化时做的操作,那也就是需要将refresh scope中的Bean 缓存失效,当再次从refresh scope中获取这个Bean时,发现取不到,就会重新触发一次Bean的初始化过程。

可以看到上面代码中有这样一句this.scope.refreshAll(),其中的scope就是RefreshScope。是用来存放scope类型为refresh类型的Bean(即使用RefreshScope注解标识的Bean),也就是说当一个Bean既不是singleton也不是prototype时,就会从自定义的Scope中去获取(Spring 允许自定义Scope),然后调用Scope的get方法来获取一个实例,Spring Cloud 正是扩展了Scope,从而控制了整个 Bean 的生命周期。当配置需要动态刷新的时候, 调用this.scope.refreshAll()这个方法,就会将整个RefreshScope的缓存清空,完成配置可动态刷新的可能。

注:关于ContextRefreshRefreshScope的初始化配置是在RefreshAutoConfiguration类中完成的。而RefreshAutoConfiguration类初始化的入口是在spring-cloud-context中的META-INF/spring.factories中配置的。从而完成整个和动态刷新相关的Bean的初始化操作。

SpringCloudConsul的配置刷新

Consul 监听配置是通过定时任务实现的,涉及的类为ConfigWatch

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ConfigWatch implements ApplicationEventPublisherAware, SmartLifecycle {}

该类的初始化是在 org.springframework.cloud.consul.config.ConsulConfigAutoConfiguration 中实现的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Bean
@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled", matchIfMissing = true)
public ConfigWatch configWatch(ConsulConfigProperties properties,
                               ConsulPropertySourceLocator locator,
                               ConsulClient consul,
                               @Qualifier(CONFIG_WATCH_TASK_SCHEDULER_NAME) TaskScheduler taskScheduler) {
  return new ConfigWatch(properties, consul, locator.getContextIndexes(), taskScheduler);
}

我们看到ConfigWatch 类实现了 ApplicationEventPublisherAwareSmartLifecycle 接口. 当应用启动后,会调用 其实现的SmartLifecyclestart 方法,然后初始化配置监听,通过向线程池添加一个定时任务,实现配置的定时拉取,定时任务默认周期是 1s

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void start() {
  if (this.running.compareAndSet(false, true)) {
    this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
      this::watchConfigKeyValues, this.properties.getWatch().getDelay());
  }
}
1、发布事件

定时任务的监听逻辑如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Timed 是 Prometheus 的监控
@Timed("consul.watch-config-keys")
public void watchConfigKeyValues() {
  if (this.running.get()) {
    // 遍历所有的配置的 key
    for (String context : this.consulIndexes.keySet()) {

      // turn the context into a Consul folder path (unless our config format
      // are FILES)
      if (this.properties.getFormat() != FILES && !context.endsWith("/")) {
        context = context + "/";
      }

      // 根据配置返回的 index 判断是否发生变化
      try {
        Long currentIndex = this.consulIndexes.get(context);
        if (currentIndex == null) {
          currentIndex = -1L;
        }

        log.trace("watching consul for context '" + context + "' with index " + currentIndex);

        // use the consul ACL token if found
        String aclToken = this.properties.getAclToken();
        if (StringUtils.isEmpty(aclToken)) {
          aclToken = null;
        }

        // 获取指定的 key
        Response<List<GetValue>> response = this.consul.getKVValues(context, aclToken, new QueryParams(this.properties.getWatch().getWaitTime(), currentIndex));

        // if response.value == null, response was a 404, otherwise it was a
        // 200
        // reducing churn if there wasn't anything
        if (response.getValue() != null && !response.getValue().isEmpty()) {
          Long newIndex = response.getConsulIndex();

          // 判断 key 的 index 是否相等,如果发生变化,则发出 RefreshEvent 事件
          if (newIndex != null && !newIndex.equals(currentIndex)) {
            // don't publish the same index again, don't publish the first
            // time (-1) so index can be primed
            // 没有发布过这个 index 的事件,且不是第一次发布
            if (!this.consulIndexes.containsValue(newIndex) && !currentIndex.equals(-1L)) {
              log.trace("Context " + context + " has new index " + newIndex);
              // 发送事件
              RefreshEventData data = new RefreshEventData(context, currentIndex, newIndex);
              this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));
            } else if (log.isTraceEnabled()) {
              log.trace("Event for index already published for context " + context);
            }
            this.consulIndexes.put(context, newIndex);
          } else if (log.isTraceEnabled()) {
            log.trace("Same index for context " + context);
          }
        } else if (log.isTraceEnabled()) {
          log.trace("No value for context " + context);
        }

      } catch (Exception e) {
        // only fail fast on the initial query, otherwise just log the error
        if (this.firstTime && this.properties.isFailFast()) {
          log.error("Fail fast is set and there was an error reading configuration from consul.");
          ReflectionUtils.rethrowRuntimeException(e);
        } else if (log.isTraceEnabled()) {
          log.trace("Error querying consul Key/Values for context '" + context + "'", e);
        } else if (log.isWarnEnabled()) {
          // simplified one line log message in the event of an agent
          // failure
          log.warn("Error querying consul Key/Values for context '" + context + "'. Message: " + e.getMessage());
        }
      }
    }
  }
  this.firstTime = false;
}

监听时会遍历所有的key,根据 key 从 Consul 获取相应的数据,判断 Index 是否发生变化,如果发生变化,则发送 RefreshEvent 事件,需要手动实现事件监听以响应配置变化。

至于spring是怎样发布事件,监听者又是怎样接收到的,这里面的细节后续有时间再详细剖析。

2、事件监听

现在我们主要来看下RefreshEvent发出去之后,监听者的逻辑。

通过函数调用栈,我们找到了这样一个监听者RefreshEventListener

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void onApplicationEvent(ApplicationEvent event) {
  if (event instanceof ApplicationReadyEvent) {
    handle((ApplicationReadyEvent) event);
  }
  else if (event instanceof RefreshEvent) {
    handle((RefreshEvent) event);
  }
}

我们知道,在spring中,监听者都需要实现这样一个方法onApplicationEvent,该方法中我们发现有这样一个分支

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
else if (event instanceof RefreshEvent) {
  handle((RefreshEvent) event);
}

这个事件就是上面发出来的,因此这里能够监听到,然后执行回调方法handle

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void handle(RefreshEvent event) {
  if (this.ready.get()) { // don't handle events before app is ready
    log.debug("Event received " + event.getEventDesc());
    Set<String> keys = this.refresh.refresh();
    log.info("Refresh keys changed: " + keys);
  }
}

public synchronized Set<String> refresh() {
  Set<String> keys = refreshEnvironment();
  this.scope.refreshAll();
  return keys;
}

主要的逻辑在refreshEnvironment方法中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public synchronized Set<String> refreshEnvironment() {
  Map<String, Object> before = extract(
      this.context.getEnvironment().getPropertySources());
  addConfigFilesToEnvironment();
  Set<String> keys = changes(before,
      extract(this.context.getEnvironment().getPropertySources())).keySet();
  this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
  return keys;
}

我们知道this.context.getEnvironment().getPropertySources()是获取env中的所有配置源,然后将其交给extract进行处理。extract方法就是将配置源中的各种格式的配置,比如map、yml、properties类型等等,统一转换为map类型,这样就可以通过统一key-value形式获取到任意想要的配置值。上面这段代码的主要逻辑就是:

  • 1、获取所有的旧的(更新之前的)配置值
  • 2、重新通过应用初始方式更新所有的配置值addConfigFilesToEnvironment
  • 3、将最新的值跟旧的值进行对比,找出所有的更新过的key
  • 4、重新发布配置变更时间EnvironmentChangeEvent,将更新过的key传递给该事件
3、Env配置更新

下面来说下第二点:重新通过应用初始方式更新所有的配置值addConfigFilesToEnvironment

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* For testing. */ ConfigurableApplicationContext addConfigFilesToEnvironment() {
  ConfigurableApplicationContext capture = null;
  try {
    StandardEnvironment environment = copyEnvironment(
        this.context.getEnvironment());
    SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class)
        .bannerMode(Mode.OFF).web(WebApplicationType.NONE)
        .environment(environment);
    // Just the listeners that affect the environment (e.g. excluding logging
    // listener because it has side effects)
    builder.application()
        .setListeners(Arrays.asList(new BootstrapApplicationListener(),
            new ConfigFileApplicationListener()));
    capture = builder.run();
    if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) {
      environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE);
    }
    MutablePropertySources target = this.context.getEnvironment()
        .getPropertySources();
    String targetName = null;
    for (PropertySource<?> source : environment.getPropertySources()) {
      String name = source.getName();
      if (target.contains(name)) {
        targetName = name;
      }
      if (!this.standardSources.contains(name)) {
        if (target.contains(name)) {
          target.replace(name, source);
        }
        else {
          if (targetName != null) {
            target.addAfter(targetName, source);
            // update targetName to preserve ordering
            targetName = name;
          }
          else {
            // targetName was null so we are at the start of the list
            target.addFirst(source);
            targetName = name;
          }
        }
      }
    }
  }
    ......
|

我们看到有这样一句SpringApplicationBuilder,这里它是生成了一个spring应用对象的生成器,然后执行它的run方法,

也就是说,这里会重新执行一遍spring的启动流程,所有的启动初始类都会重新执行,包括上面提到的ConsulPropertySourceLocator类的locate方法,这里就会再次向consul server发起请求获取最新的配置数据,写入env中。

因此后面通过this.context.getEnvironment().getPropertySources()得到的就是最新的配置源了。同时业务中也可以通过context.getEnvironment().getProperty(key)拿到任意key的最新值了。

刷新scope域

在上面的refresh方法中,我们还剩下这样一句没有讲解:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.scope.refreshAll();

这里主要就是刷新spring容器该scope类型下的所有bean,就可以通过@RefreshScope的bean实例的get方法获取到最新的值了。前提条件是我们需要监听这个事件RefreshScopeRefreshedEvent

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@EventListener(classes = RefreshScopeRefreshedEvent.class)
public void updateChange(RefreshScopeRefreshedEvent event) {
  //这里获取到的新的值
  String pwd = redisProperties.getPassword();
  System.out.print("new pwd: " + pwd);
}

上面的EnvironmentChangeEvent这个事件发生时,@RefreshScope的bean实例还是老的bean,在这个事件里拿到的还是老的值:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@EventListener(classes = EnvironmentChangeEvent.class)
public void updateChange(EnvironmentChangeEvent event) {
  Set<String> updatedKeys = event.getKeys();
  System.out.print(updatedKeys);
  for (String key : updatedKeys) {
    if (key.equals("redis.password")) {
      System.out.print("new password: " + context.getEnvironment().getProperty(key));
      //    do something
    }
  }
  //这里获取到的还是旧的值
  String pwd = redisProperties.getPassword();
  System.out.print("old pwd: " + pwd);
}

具体是怎样刷新scope域的,后面有时间再专门讲解。

参考链接: https://juejin.im/post/6845166890461954062
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 有文化的技术人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
常用 mongo 操作实际操练
查询数据的时候我们发现,mongodb 自动在我们的文档中加入了 _id 字段,这是一个主键,如果不自己设置的话,mongodb 为默认给我们加上,是一个 24 位的 uuid
阿兵云原生
2023/02/16
2920
硬货来了!轻松掌握 MongDB 流式聚合操作
信息科学中的聚合是指对相关数据进行内容筛选、处理和归类并输出结果的过程。MongoDB 中的聚合是指同时对多个文档中的数据进行处理、筛选和归类并输出结果的过程。数据在聚合操作的过程中,就像是水流过一节一节的管道一样,所以 MongoDB 中的聚合又被人称为流式聚合。
崔庆才
2019/10/08
4.8K0
硬货来了!轻松掌握 MongDB 流式聚合操作
MongoDB 统计 group 操作用不了,试试 mapReduce 吧
今天,同事小张 Q 我, 说自己辛苦花了一天的时间,基于 mongodb 数据库开发的待办统计功能一直报错!
猿芯
2020/07/06
1.1K0
MongoDB中MapReduce使用
玩过Hadoop的小伙伴对MapReduce应该不陌生,MapReduce的强大且灵活,它可以将一个大问题拆分为多个小问题,将各个小问题发送到不同的机器上去处理,所有的机器都完成计算后,再将计算结果合并为一个完整的解决方案,这就是所谓的分布式计算。本文我们就来看看MongoDB中MapReduce的使用。 ---- mapReduce MongoDB中的MapReduce可以用来实现更复杂的聚合命令,使用MapReduce主要实现两个函数:map函数和reduce函数,map函数用来生成键值对序列,map函
江南一点雨
2018/04/02
1.5K0
Mongo散记–聚合(aggregation)&amp; 查询(Query)
工作中使用到Mongo,可是没有系统的学习研究过Mongo,仅对工作过程中,在Mongo的使用过程中的一些知识点做一下记录,并随时补充,达到总结备忘的目的。
全栈程序员站长
2022/07/12
2.5K0
【翻译】MongoDB指南/聚合——聚合管道
【原文地址】https://docs.mongodb.com/manual/ 聚合 聚合操作处理数据记录并返回计算后的结果。聚合操作将多个文档分组,并能对已分组的数据执行一系列操作而返回单一结果。MongoDB提供了三种执行聚合的方式:聚合管道,map-reduce方法和单一目的聚合操作。 聚合管道 MongoDB的聚合框架模型建立在数据处理管道这一概念的基础之上。文档进入多阶段管道中,管道将文档转换为聚合结果。最基本的管道阶段类似于查询过滤器和修改输出文档形式的文档转换器。 其他的管道为分组和排序提供一些
甜橙很酸
2018/03/08
4.1K0
【翻译】MongoDB指南/聚合——聚合管道
MongoDB中$type、索引、聚合
再次执行db.col.find({“title” : {$type : 2}}).pretty();
别团等shy哥发育
2023/02/25
1.7K0
MongoDB中$type、索引、聚合
mongodb11天之屠龙宝刀(六)mapreduce:mongodb中mapreduce原理与操作案例
mongodb11天之屠龙宝刀(六)mapreduce:mongodb中mapreduce原理与操作案例 一 Map/Reduce简介 MapReduce 是Google公司的核心模型,用于大规模数据集(大于1TB)的并行计算。“映射(Map)”与“化简(Reduce)”的概念是它们的主要思想。MapReduce使用JavaScript作为“查询语言”,能够在多台服务器之间并行执行。MapReduce将负责的运行于大规模集群上的并行计算过程高度地抽象为两个函数(Map和Reduce),利用一个输入<
学到老
2018/03/19
2.1K0
mongodb11天之屠龙宝刀(六)mapreduce:mongodb中mapreduce原理与操作案例
MongoDB 聚合管道(Aggregation Pipeline)
管道概念 POSIX多线程的使用方式中, 有一种很重要的方式-----流水线(亦称为“管道”)方式,“数据元素”流串行地被一组线程按顺序执行。它的使用架构可参考下图: 以面向对象的思想去理解,整个流水
张善友
2018/01/29
2.9K0
MongoDB 聚合管道(Aggregation Pipeline)
MongoDB系列六(聚合).
 一、概念     使用聚合框架可以对集合中的文档进行变换和组合。基本上,可以用多个构件创建一个管道(pipeline),用于对一连串的文档进行处理。这些构件包括筛选(filtering)、投射(projecting)、分组(grouping)、排序(sorting)、限制(limiting)和跳过(skipping)。 二、聚合函数 db.driverLocation.aggregate( {"$match":{"areaCode":"350203"}}, {"$project":{"dr
JMCui
2018/04/23
5K0
MongoDB系列六(聚合).
【mongo 系列】索引浅析
B Tree就是一种常用的数据库索引数据结构,MongoDB采用 B 树做索引,索引创建在colletions 上。
阿兵云原生
2023/02/16
1.7K0
在MongoDB中实现聚合函数
随着组织产生的数据爆炸性增长,从GB到TB,从TB到PB,传统的数据库已经无法通过垂直扩展来管理如此之大数据。传统方法存储和处理数据的成本将会随着数据量增长而显著增加。这使得很多组织都在寻找一种经济的解决方案,比如NoSQL数据库,它提供了所需的数据存储和处理能力、扩展性和成本效率。NoSQL数据库不使用SQL作为查询语言。这种数据库有多种不同的类型,比如文档结构存储、键值结构存储、图结构、对象数据库等等。 我们在本文中使用的NoSQL是MongoDB,它是一种开源的文档数据库系统,开发语言为C++。它提供
张善友
2018/01/22
3.9K0
MongoDB必备知识点全面总结
传统的关系型数据库,比如说MySQL,我们已经用的非常熟悉了,那么我们在什么时候需要用到MongoDB呢?传统的关系型数据库在数据操作的“三高”需求以及应对Web2.0的网站需求面前,显得力不从心。
害恶细君
2022/11/22
3.9K0
MongoDB必备知识点全面总结
MongoDB学习|知识点整理
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
技术从心
2019/08/06
2K0
Python | Python交互之mongoDB交互详解
本篇为mongodb篇,包含实例演示,mongodb高级查询,mongodb聚合管道,python交互等内容。
咸鱼学Python
2019/10/09
8.1K0
MongoDB核心概念与基本操作
​   mongodb中的库就类似于传统关系型数据库中库的概念,用来通过不同库隔离不同应用数据。mongodb中可以建立多个数据库。每一个库都有自己的集合和权限,不同的数据库也放置在不同的文件中。默认的数据库为"test",数据库存储在启动指定的data目录中。
别团等shy哥发育
2023/02/25
1.5K0
MongoDB核心概念与基本操作
初试MongoDB数据库
特征: 使用键值(Key Value)储存数据; MongoDB的逻辑结构是一种层次结构,主要由:文档(document)、集合(collection)、数据库(database)这三部分组成的。
九旬
2020/10/23
1.3K0
初试MongoDB数据库
了解 MongoDB 看这一篇就够了
MongoDB 是一款流行的开源文档型数据库,从它的命名来看,确实是有一定野心的。MongoDB 的原名一开始来自于 英文单词"Humongous", 中文含义是指"庞大",即命名者的意图是可以处理大规模的数据。
美码师
2019/10/18
1.3K0
了解 MongoDB 看这一篇就够了
MongoDB 新功能介绍-Change Streams
MongoDB 3.6已经GA有一段时间,网络上对于该版本新特性的详细介绍文章比较少为此借机会对部分新特性做一个相对详细的介绍。基于早期MongoDB版本实现如跨平台数据同步、消息通知、ETL及oplog备份等服务时大多依赖于 Tailable Cursors 的方式。当然这样的实现一来相对复杂同时也存在着一些风险(如不同版本oplog兼容性及过滤特定操作类型等)。
MongoDB中文社区
2018/08/14
2.2K0
MongoDB 新功能介绍-Change Streams
MongoDB 数据库的学习与使用详解
​ MongoDB 数据库是一种 NOSQL 数据库,NOSQL 数据库不是这几年才有的,从数据库的初期发展就以及存在了 NOSQL 数据库。数据库之中支持的 SQL 语句是由 IBM 开发出来的,并且最早就应用在了 Oracle 数据库,但是 SQL 语句的使用并不麻烦,就是几个简单的单词:SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY,但是在这个时候有人开始反感于编写 SQL 操作。于是有一些人就开始提出一个理论 —— 不要去使用 SQL ,于是最早的 NOSQL 概念产生了。可是后来的发展产生了一点变化,在 90 年代到 2010 年之间,世界上最流行的数据库依然是关系型数据库,并且围绕着关系型数据库开发出了大量的程序应用。后来又随着移动技术(云计算、大数据)的发展,很多公司并不愿意去使用大型的厂商数据库 —— Oracle 、DB2,因为这些人已经习惯于使用 MYSQL 数据库了,这些人发现在大数据以及云计算的环境下,数据存储受到了很大的挑战,那么后来就开始重新进行了 NOSQL 数据库的开发,但是经过长期的开发,发现 NOSQL 数据库依然不可能离开传统的关系型数据库 (NOSQL = Not Only SQL)。
星哥玩云
2022/08/18
2.1K0
MongoDB 数据库的学习与使用详解
相关推荐
常用 mongo 操作实际操练
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 前言
    • spring cloud config简介
  • 启动加载扩展点
  • SpringCloudConsul的配置加载
  • 配置动态刷新
  • SpringCloudConsul的配置刷新
    • 1、发布事件
    • 2、事件监听
    • 3、Env配置更新
  • 刷新scope域
    • 参考链接: https://juejin.im/post/6845166890461954062
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档