多线程程序如何简单高效地读取配置中心上的配置?

本文限定为主动从配置中心读取配置方法,不限定配置中心类型,比如可使用DB作为配置中心。

程序和配置中心间存在网络开销,因此需避免每次业务处理或请求都从配置中心读取配置。

规避网络开销,可采取本地缓存配置,每隔指定时间只读取一次配置中心,比如每秒读取一次配置中心。

假设每秒读取一次配置中心,这样每次的开销减少到每秒只有一次网络开销,此时可观察到性能毛刺,这毛刺是因为每次读取配置中心时的性能抖动(下降)。

需要将读取配置中心从业务线程中移除,为此引入配置线程,由配置线程专门负责从配置中心读取配置,业务线程不直接从配置中心读取配置。

struct Config { // 配置
  string a;
  string b;
};

class ConfigThread { // 配置线程
public:
  void run() {
    while (!stop()) {
      std::this_thread::sleep_for(std::chrono::milliseconds(1000));
      read_config_from_config_center(); // 从配置中心读取配置

      std::unique_lock<std::shared_mutex> lock(_mutex);
      update_config(); // &#x66F4;&#x65B0;&#x672C;&#x5730;&#x7684; _config
    }
  }

  void get_config(struct Config* config) const {
    std::shared_lock<std::shared_mutex> lock(_mutex);
    *config = _config;
  }

private:
  // &#x6CE8;&#xFF1A;C++17 &#x624D;&#x652F;&#x6301; shared_mutex &#x9501;
  mutable std::shared_mutex _mutex; // &#x7528;&#x6765;&#x4FDD;&#x62A4; _config &#x7684;&#x8BFB;&#x53D6;&#x5199;&#x9501;
  struct Config _config;
};

class WorkThread { // &#x4E1A;&#x52A1;&#x7EBF;&#x7A0B;
public:
  void run() {
    while (!stop()) {
      struct Config config;
      _config_thread->get_config(&config);
    }
  }

private:
  std::shared_ptr<configthread> _config_thread;
};
</configthread></std::shared_mutex></std::shared_mutex>

从上面代码可以看到,有了新的矛盾点,即读取锁碰撞问题(注:锁带来的性能问题主要是锁碰撞,而非一次系统调用)。

当配置线程在加写锁更新配置时,仍然会产生毛刺。简单点可使用读优先读写锁来降低影响,但不能根本解决问题。

可使用尝试锁替代读锁来根本性解决问题:

struct Config { // &#x914D;&#x7F6E;
  string a;
  string b;
};

class ConfigThread { // &#x914D;&#x7F6E;&#x7EBF;&#x7A0B;
public:
  void run() {
    while (!stop()) {
      std::this_thread::sleep_for(std::chrono::milliseconds(1000));
      read_config_from_config_center(); // &#x4ECE;&#x914D;&#x7F6E;&#x4E2D;&#x5FC3;&#x8BFB;&#x53D6;&#x914D;&#x7F6E;

      std::unique_lock<std::shared_mutex> lock(_mutex);
      update_config(); // &#x66F4;&#x65B0;&#x672C;&#x5730;&#x7684; _config
    }
  }

  bool get_config(struct Config* config) const {
    if (!_mutex.try_lock_shared())
      return false;
    *config = _config;
    _mutex.unlock();
    return true
  }

private:
  mutable std::shared_mutex _mutex; // &#x7528;&#x6765;&#x4FDD;&#x62A4; _config &#x7684;&#x8BFB;&#x53D6;&#x5199;&#x9501;
  struct Config _config;
};

class WorkThread { // &#x4E1A;&#x52A1;&#x7EBF;&#x7A0B;
public:
  void run() {
    while (!stop()) {
      const struct Config& config = get_config();
    }
  }

private:
  const struct Config& get_config() {
    struct Config config;
    if (_config_thread->get_config(&config))
      _config = config;
    // &#x5982;&#x679C;&#x6CA1;&#x6709;&#x8BFB;&#x53D6;&#x5230;&#x65B0;&#x7684;&#x914D;&#x7F6E;&#xFF0C;&#x5219;&#x4ECD;&#x7136;&#x4F7F;&#x7528;&#x8001;&#x7684;&#x914D;&#x7F6E;
    return _config;
  }

private:
  std::shared_ptr<configthread> _config_thread;
  struct Config _config; // &#x7EBF;&#x7A0B;&#x672C;&#x5730;&#x914D;&#x7F6E;
};
</configthread></std::shared_mutex>

上述 WorkThread::get_config() 每次都要加一次锁,因为加的是读尝试锁,所以对性能影响很少,大多数都可忽略。如果仍然对性能有影响,可采取每秒只调用一次 ConfigThread::get_config(),来减少锁的调用次数。

局限性:
本文介绍的方法仅适合配置比较少的场景,是多还是少,主要依据消耗的内存大小,当消耗的内存在可接受范围内则可定义为少。

此外,一个问题是:配置更新时间点不一致,但即使每次都去读取配置中心,仍然不一致问题。

Original: https://www.cnblogs.com/aquester/p/14155345.html
Author: #NAME?
Title: 多线程程序如何简单高效地读取配置中心上的配置?

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/541770/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Vue3+Vue-cli4项目中使用腾讯滑块验证码

    Vue3+Vue-cli4项目中使用腾讯滑块验证码 简介: 滑块验证码相比于传统的图片验证码具有以下优点: 验证码的具体验证不需要服务端去验证,服务端只需要核验验证结果即可。 验证…

    Java 2023年6月8日
    078
  • Spring源码学习笔记4——BeanFactoryPostProcessor执行

    一丶BeanFactoryPostProcessor是什么 Spring留给我们的一个扩展接口,在BeanDefinition加载注册完之后,并执行一些前置操作(笔记3)之后会反射…

    Java 2023年6月14日
    089
  • Jrebel 工具学习

    Jrebel 可快速实现热部署 ,节省了大量重启时间,提高了个人开发效率。网上可搜索到破解版 。 Original: https://www.cnblogs.com/FCWORLD…

    Java 2023年6月6日
    0122
  • 【实战】利用多线程优化查询百万级数据

    前言 日常开发中,难免会遇到需要查询到数据库所有记录的业务场景,在索引完善的情况下,当数据量达到百万级别或者以上的时候,全表查询就需要耗费不少的时间,这时候我们可以从以下几个方向着…

    Java 2023年6月5日
    088
  • Spring的ApplicationEvent实现

    原理:ApplicationContextAware接口提供了publishEvent方法,实现了Observe(观察者)设计模式的传播机制,实现了对bean的传播。通过Appli…

    Java 2023年5月30日
    072
  • Kafka 延时队列&重试队列

    一、延时队列 1. 简介 TimingWheel是kafka时间轮的实现,内部包含了⼀个TimerTaskList数组,每个数组包含了⼀些链表组成的TimerTaskEntry事件…

    Java 2023年6月5日
    092
  • 线程池使用

    线程池 1.工具类实现 undefined 线程池监控: long activeCount = ((ThreadPoolExecutor)instance).getActiveCo…

    Java 2023年6月9日
    081
  • Java实现动态数组【数据结构与算法】

    1、数组 类型固定、长度固定 连续的内存空间 顺序存储、随机读取 查询快、新增删除慢。 最好初始化的时候就指定数组大小。这样就可以避免一定的数组扩容出现的内存消耗。 import …

    Java 2023年6月15日
    077
  • Java中的包装类(Interage和String)

    包装类的分类 包装类的特点 包装类有了类的属性就可以调用类中的方法,更有了类的特点,还可以进行类型的互相转换,更方便我们使用。 包装类和基本数据类型的转换 jdk5前的手动装箱和拆…

    Java 2023年6月6日
    071
  • auth授权登录:淘宝客官方接口中用到的sessionkey是如何获取的

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年5月30日
    074
  • 基于数据库的代码自动生成工具,生成JavaBean、生成数据库文档、生成前后端代码等(v5.8.8版)

    TableGo v5.8.8版震撼发布,此次版本更新如下:1、新增两个扩展字段,用于生成自定义模板时使用。2、自定义模板新增模板目录,可以选择不同分类目录下的模板。3、自定义模板生…

    Java 2023年6月9日
    0101
  • MySQL常用知识点梳理

    删表 DROP TABLE IF EXISTS 表名; 新建表create table 表名(字段名 类型 约束(逐渐,非空,唯一 ,默认值),字段名 类型 约束(逐渐,非空,唯一…

    Java 2023年6月16日
    063
  • 集合remove()方法相关问题

    学习集合的过程中,了解到一个有关于remove()方法的有关特性,特此记录 首先remove方法的格式: collection.remove(Object o); 这是指对集合co…

    Java 2023年6月7日
    092
  • JDK源码分析实战系列-ThreadLocal

    公众号原文链接* 总览 ThreadLocal提供了线程局部变量的解决方案。 我们知道成员变量在多线程下是不安全的,而局部变量的生命周期又取决于变量定义的范围。那么有没有一个变量定…

    Java 2023年6月13日
    059
  • System.out.printf使用以及注意点

    一、System.out.printf格式化输出 1、常用控制符 控制符 说明 %d 按十进制整型数据的实际长度输出。 %ld 输出长整型数据。 %md m 为指定的输出字段的宽度…

    Java 2023年6月5日
    085
  • Java HashMap 四个构造函数

    HashMap():构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。 HashMap(int initialCapacity):构造一个空的 Has…

    Java 2023年6月5日
    079
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球