相信在实际工作中,提及性能优化你会想到代码优化,但是实际上有些性能优化可能只需要调整一些配置参数就可以搞定了,为什么这么说呢?我给你举几个例子:

你可以调整配置的超时时间,让请求快速失败,防止系统的雪崩,提升系统的可用性;
你还可以调整 HTTP 客户端连接池的大小,来提升调用第三方 HTTP 服务的并行处理能力,从而提升系统的性能。

你可以认为,配置是管理你系统的工具,在你的垂直电商系统中,一定会有非常多的配置项,比如数据库的地址、请求 HTTP 服务的域名、本地内存最大缓存数量等等。

那么,你要如何对这些配置项做管理呢?管理的过程中要注意哪些事情呢?

如何对配置进行管理呢?

配置管理由来已久,比如在 Linux 系统中就提供了大量的配置项,你可以依据自身业务的实际情况,动态地对系统功能做调整。比如,你可以修改 dirty_writeback_centisecs 参数的数值,就可以调整 Page Cache 中脏数据刷新到磁盘上的频率;你也可以通过修改tcp_max_syn_backlog 参数置的值,来调整未建立连接队列的长度。修改这些参数既可以通过修改配置文件并且重启服务器来配置生效,也可以通过 sysctl 命令来动态地调整,让配置即时生效。

那么你在开发应用的时候,都有哪些管理配置的方式呢?主要有两种:

一种是通过配置文件来管理;
另一种是使用配置中心来管理。

以电商系统为例,你和你的团队在刚开始研发垂直电商系统时,为了加快产品的研发速度,大概率不会注意配置管理的问题,会自然而然地把配置项和代码写在一起。但是随着配置项越来越多,为了更好地对配置项进行管理,同时避免修改配置项后还要对代码做重新的编译,你选择把配置项独立成单独的文件(文件可以是 properties 格式、xml 格式或 yaml格式)。不过,这些文件还是会和工程一起打包部署,只是更改配置后不需要对代码重新编译了。

随后,你很快发现了一个问题:虽然把配置拆分了出来,但是由于配置还是和代码打包在一起,如果要更改一个配置,还是需要重新打包,这样无疑会增加打包的时间。于是,你考虑把配置写到单独的目录中,这样,修改配置就不需要再重新打包了(不过,由于配置并不能够实时地生效,所以想让配置生效,还是需要重启服务)。

我们一般使用的基础组件,比如 Tomcat,Nginx,都是采用上面这种配置文件的方式来管理配置项的,而在 Linux 系统中,我提到的 tcp_max_syn_backlog 就可以配置在/etc/sysctl.conf 中。

这里,我需要强调一点,我们通常会把配置文件存储的目录,标准化为特定的目录。比如,都配置成 /data/confs 目录,然后把配置项使用 Git 等代码仓库管理起来。这样,在增加新的机器时,在机器初始化脚本中,只需要创建这个目录,再从 Git 中拉取配置就可以了,是一个标准化的过程,这样可以避免在启动应用时忘记部署配置文件。

另外,如果你的服务是多机房部署的,那么不同机房的配置项中,有可能有相同的,或者有不同的。那么,你需要将相同的配置项放置在一个目录中给多个机房公用,再将不同的配置项,放置在以机房名为名称的目录中。在读取配置的时候应该优先读取机房的配置,再读取公共配置,这样可以减少配置文件中的配置项的数量。

我给你列了一个典型目录配置,如果你的系统也使用文件来管理配置,那么可以参考一下。

/data/confs/common/commerce // 电商业务的公共配置
/data/confs/commerce-zw // 电商业务兆维机房配置
/data/confs/commerce-yz // 电商业务亦庄机房配置
/data/confs/common/community // 社区业务的公共配置
/data/confs/community-zw // 社区业务兆维机房配置
/data/confs/community-yz // 社区业务亦庄机房配置

那么,这是不是配置管理的最终形态呢?当然不是,你不要忘了把配置放在文件中的方式还存在的问题(我上面也提到过了),那就是,我们必须将服务重启后,才能让配置生效。有没有一种方法可以在不重启应用的前提下,也能让配置生效呢?这就需要配置中心帮助我们实现了。

配置中心是如何实现的?

配置中心可以算是微服务架构中的一个标配组件了。业界也提供了多种开源方案供你选择,比较出名的有携程开源的 Apollo,百度开源的 Disconf,360 开源的 QConf,SpringCloud 的组件 Spring Cloud Config 等等。

在我看来,Apollo 支持不同环境,不同集群的配置,有完善的管理功能,支持灰度发布、更改热发布等功能,在所有配置中心中功能比较齐全,推荐你使用。

那么,配置中心的组件在实现上,有哪些关键的点呢?如果你想对配置中心组件有更强地把控力,想要自行研发一套符合自己业务场景的组件,又要如何入手呢?

配置信息如何存储

其实,配置中心和注册中心非常类似,其核心的功能就是对于配置项的存储和读取。所以,在设计配置中心的服务端时,我们需要选择合适的存储组件,来存储大量的配置信息,这里可选择的组件有很多。

事实上,不同的开源配置中心也使用了不同的组件,比如 Disconf、Apollo 使用的是MySQL;QConf 使用的是 ZooKeeper。我之前维护和使用的配置中心就会使用不同的存储组件,比如微博的配置中心使用 Redis 来存储信息,而美图的则使用 Etcd。

而无论使用哪一种存储组件,你所要做的就是规范配置项在其中的存储结构。比如,我之前使用的配置中心用 Etcd 作为存储组件,支持存储全局配置、机房配置和节点配置。其中,节点配置优先级高于机房配置,机房配置优先级高于全局配置。也就是说,我们会优先读取节点的配置,如果节点配置不存在,再读取机房配置,最后读取全局配置。它们的存储路径如下:

/confs/global/{env}/{project}/{service}/{version}/{module}/{key} // 全局配置
/confs/regions/{env}/{project}/{service}/{version}/{region}/{module}/{key} // 区域配置
/confs/nodes/{env}/{project}/{service}/{version}/{region}/{node}/{module}/{key} // 区域节点配置

变更推送如何实现

配置信息存储之后,我们需要考虑如何将配置的变更推送给服务端,这样就可以实现配置的动态变更,不需要重启服务器就能让配置生效了。而我们一般会有两种思路来实现变更推送:一种是轮询查询的方式;一种是长连推送的方式。

轮询查询很简单,就是应用程序向配置中心客户端注册一个监听器,配置中心的客户端,定期地(比如 1 分钟)查询所需要的配置是否有变化,如果有变化则通知触发监听器,让应用程序得到变更通知。

这里有一个需要注意的点,如果有很多应用服务器都去轮询拉取配置,由于返回的配置项可能会很大,那么配置中心服务的带宽就会成为瓶颈。为了解决这个问题,我们会给配置中心的每一个配置项,多存储一个根据配置项计算出来的 MD5 值。

配置项一旦变化,这个 MD5 值也会随之改变。配置中心客户端在获取到配置的同时,也会获取到配置的 MD5 值,并且存储起来。那么在轮询查询的时候,需要先确认存储的 MD5值,和配置中心的 MD5 是不是一致的。如果不一致,这就说明配置中心中,存储的配置项有变化,然后才会从配置中心拉取最新的配置。

由于配置中心里存储的配置项变化的几率不大,所以使用这种方式后,每次轮询请求就只是返回一个 MD5 值,可以大大地减少配置中心服务器的带宽。

另一种长连的方式,则是在配置中心服务端保存每个连接关注的配置项列表。这样,当配置中心感知到配置变化后,就可以通过这个连接,把变更的配置推送给客户端。这种方式需要保持长连,也需要保存连接和配置的对应关系,实现上要比轮询的方式复杂一些,但是相比轮询方式来说,能够更加实时地获取配置变更的消息。

而在我看来,配置服务中存储的配置变更频率不高,所以对于实时性要求不高,但是期望实现上能够足够简单,所以如果选择自研配置中心的话,可以考虑使用轮询的方式。

如何保证配置中心高可用

除了变更通知以外,在配置中心实现中,另外一个比较关键的点在于如何保证它的可用性,因为对于配置中心来说,它的可用性的重要程度要远远大于性能。这是因为我们一般会在服务器启动时,从配置中心中获取配置,如果配置获取的性能不高,那么外在的表现也只是应用启动时间慢了,对于业务的影响不大;但是,如果获取不到配置,很可能会导致启动失败。

比如,我们把数据库的地址存储在了配置中心里,如果配置中心宕机导致我们无法获取数据库的地址,那么自然应用程序就会启动失败。因此,我们的诉求是让配置中心“旁路化”。

也就是说,即使配置中心宕机,或者配置中心依赖的存储宕机,我们仍然能够保证应用程序是可以启动的。那么这是如何实现的呢?

我们一般会在配置中心的客户端上,增加两级缓存:第一级缓存是内存的缓存;另外一级缓存是文件的缓存。

配置中心客户端在获取到配置信息后,会同时把配置信息同步地写入到内存缓存,并且异步地写入到文件缓存中。内存缓存的作用是降低客户端和配置中心的交互频率,提升配置获取的性能;而文件的缓存的作用就是灾备,当应用程序重启时,一旦配置中心发生故障,那么应用程序就会优先使用文件中的配置,这样虽然无法得到配置的变更消息(因为配置中心已经宕机了),但是应用程序还是可以启动起来的,算是一种降级的方案。

小结

在这节中,我们了解了系统开发的过程中,我们是如何管理大量的配置项的,你需要了解的重点是:

1、配置存储是分级的,有公共配置,有个性的配置,一般个性配置会覆盖公共配置,这样可以减少存储配置项的数量;

2、配置中心可以提供配置变更通知的功能,可以实现配置的热更新;

3、配置中心关注的性能指标中,可用性的优先级是高于性能的,一般我们会要求配置中心的可用性达到 99.999%,甚至会是 99.9999%。

这里你需要注意的是,并不是所有的配置项都需要使用配置中心来存储,如果你的项目还是使用文件方式来管理配置,那么你只需要,将类似超时时间等,需要动态调整的配置,迁移到配置中心就可以了。对于像是数据库地址,依赖第三方请求的地址,这些基本不会发生变化的配置项,可以依然使用文件的方式来管理,这样可以大大地减少配置迁移的成本。

高并发系统设计二十六(配置中心)相关推荐

  1. 高并发系统设计二十五(API网关)

    到目前为止,你的垂直电商系统在经过微服务化拆分之后,已经运行了一段时间了,系统的扩展性得到了很大的提升,也能够比较平稳地度过高峰期的流量了. 不过最近你发现,随着自己的电商网站知名度越来越高,系统迎来 ...

  2. 聊聊高并发(二十六)解析java.util.concurrent各个组件(八) 理解CountDownLatch闭锁

    CountDownLatch闭锁也是基于AQS实现的一种同步器,它表示了"所有线程都等待,直到锁打开才继续执行"的含义.它和Semaphore的语意不同, Semaphore的获取 ...

  3. 聊聊高并发(二十五)解析java.util.concurrent各个组件(七) 理解Semaphore

    前几篇分析了一下AQS的原理和实现,这篇拿Semaphore信号量做例子看看AQS实际是如何使用的. Semaphore表示了一种可以同时有多个线程进入临界区的同步器,它维护了一个状态表示可用的票据, ...

  4. 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁

    上一篇聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁 讲了可重入读写锁的基本情况和主要的方法,显示了如何 ...

  5. 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁...

    上一篇聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁 讲了可重入读写锁的基本情况和基本的方法,显示了怎样 ...

  6. 聊聊高并发(三十六)Java内存模型那些事(四)理解Happens-before规则

    在前几篇将Java内存模型的那些事基本上把这个域底层的概念都解释清楚了,聊聊高并发(三十五)Java内存模型那些事(三)理解内存屏障 这篇分析了在X86平台下,volatile,synchronize ...

  7. 聊聊高并发(二十二)解析java.util.concurrent各个组件(四) 深入理解AQS(二)

    上一篇介绍了AQS的基本设计思路以及两个内部类Node和ConditionObject的实现 聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一) 这篇 ...

  8. 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类

    这篇说说java.util.concurrent.atomic包里的类,总共12个,网上有很多文章解析这几个类,这里挑些重点说说. 这12个类可以分为三组: 1. 普通类型的原子变量 2. 数组类型的 ...

  9. 聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁

    这篇讲讲ReentrantReadWriteLock可重入读写锁,它不仅是读写锁的实现,并且支持可重入性. 聊聊高并发(十五)实现一个简单的读-写锁(共享-排他锁) 这篇讲了如何模拟一个读写锁. 可重 ...

最新文章

  1. DX使用随记--GroupControl
  2. 深入理解Java内存模型(七)——总结
  3. mysql 执行计划详解,Mysql中的explain执行计划详解(1)
  4. JQuery Datatables 在Bootstrap tab中列名无法对齐的问题
  5. 【C】课堂结对联系-求整数数组的子数组之和的最大值(党云龙、黄为)
  6. 怎么用计算机连接电视,电视当电脑显示器怎么连接
  7. windowsXP的所有应用命令
  8. hdu 1099 Lottery
  9. 如何制作Win10启动U盘启动盘iso镜像文件
  10. 用excel制作门店流水日常
  11. 质数筛+埃氏筛取思想
  12. 苹果手机用计算机打不开,苹果手机更新ios11后,照片在电脑上打不开怎么办?...
  13. 基于哈里斯鹰算法优化概率神经网络PNN的分类预测-附代码
  14. VUE下载excel文件
  15. 教你把竖屏视频剪辑为横屏播放的方法
  16. 三型两步验证登不上_苹果手机开启了双重认证,未信任的设备之前登入了,开启了双重认证未...
  17. Vue CLI3不兼容IE11打开空白报错SCRIPT1003: 缺少 ‘:‘
  18. 把泛型传给泛型方法。
  19. EasyUI之Linkbutton按钮
  20. [ctfhub]刷题记录

热门文章

  1. freemarker导出word、word转pdf,带附件、图片等比缩放
  2. java面试(3)SQL优化
  3. 华为路由器忘记密码_如果忘记密码,如何访问路由器
  4. python接外包项目_微信外包项目,接口开发流程小记
  5. [差分 上下界最大流] SRM 694 div1 SRMDiv0Easy
  6. 网红蛋糕店整体室内装修施工图+效果图
  7. Quick BI 数据大屏快速入门
  8. DotProject首页、文档和下载 - 项目管理工具 - 开源中国社区
  9. Leetcode_34_Search for a Range
  10. 福昕阅读器如何设置背景和文字颜色