应用程序配置config最佳实践

背景&问题

对于一个系统来说, 配置文件是必不可少的, 有的系统是把配置文件放到conf文件夹下面, 系统启动的时候加载进来, 有的是统一放到外部的某个地方, 方便统一管理. 这样设计配置文件的问题显而易见, 一旦需要修改配置文件, 需要重启或重新部署该应用. 重启你懂的, 用户们在使用呢, 怎么能突然no service呢. 金主爸爸们不能得罪啊, 难道要半夜起来重启吗? 有没有什么好的方法可以无需重启就直接无缝衔接到新的配置项呢?

最近在看Microsoft的CloudDesignPattern, 写的非常详细, 各种云计算中的软件开发最佳实践都提到了, 推荐大家下载PDF查阅. 这本手册中就有提到Runtime Reconfiguration Pattern, 结合工作中的实践来做一个小小的总结.

解决方法

step0: 前提

首先需要一套全局配置中心, 专门用于存储公司内部各个应用的配置项, 解决配置混乱分散的问题. 用户可以在配置中心创建和修改配置, 修改配置有相应的版本控制和容灾措施. 有了这套系统之后, 其他应用就可以通过API获取配置项. 具体的配置中心搭建可以通过淘宝开源的Diamond来实现, 这个人写的XDiamond也很清晰(http://blog.csdn.net/hengyunabc/article/details/47777807)

有了这套中心化的配置管理服务, 就可以开始安全, 高效, 实时地获取配置了. 通过调用全局配置的API, 获取该项目的配置项, 这个过程中也有许多注意事项.

step1: 可靠地获取配置

有了上述的配置系统, 剩下的事情就是怎么call API的问题了. 通过HTTP请求获取配置信息, 难免遇到网络拥塞, 请求失败, 或者server一时间忙碌, 无响应等. 怎么避免这些并非client side的问题导致配置项获取不到呢? 配置项对程序运行非常重要, 怎么保证能够更加可靠地获取呢?

  • 首先想到的就是Retry. 如果这个请求不是我的问题, 是服务器的问题, 或者网络问题, 重新请求一次会成功的话, 那么可以选择一定的backoff时间后重试.
  • 重试不能一直无限重试下去. 需要设定一个timeout的时间, 如果多次尝试都没有结果, 说明服务挂了, 或者系统出现其他的故障. 必要的添加一些logging, 比如请求失败的原因, 方便后续发现问题.
  • 并非所有的请求失败都要重试. 针对特定的HTTP code来重发请求, 比如请求返回401(unauthorized), 重试多少次都是没有用的, 因为你的认证没有通过. 一般来说, 500, 502, 504这些server端的问题, 可以进行重试.
    通过下面的retry session, 一股脑解决上述问题!
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry


def requests_retry_session(
    retries=3,
    backoff_factor=0.3,
    status_forcelist=(500, 502, 504),
    session=None,
):
    session = session or requests.Session()
    retry = Retry(
        total=retries,
        read=retries,
        connect=retries,
        backoff_factor=backoff_factor,
        status_forcelist=status_forcelist,
    )
    adapter = HTTPAdapter(max_retries=retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)
    return session

这里有个高大上的python retry best practice. 覆盖了重试过程中需要考虑的各种问题, 有空可以研读一下https://www.peterbe.com/plog/best-practice-with-retries-with-requests

step2: 有效地更新配置

获取应用的配置信息, 可以通过主动pull的方式, 也可以通过被动push的方式, push方式需要配置管理中心有消息通知的服务.
简单说说主动去pull配置的一些注意事项吧.

  • 间隔一定的时间去更新缓存的配置. HTTP请求难免会花费一定的时间, 遇到网络不好的时候, 请求配置时间会更加长, 因此会给你的应用程序带来一定的延迟. 我们可以把获取到的配置缓存到内存中, 超过一定的时间后可以再重新获取并更新.
import time
class ConfigReader(object):
    # 初始化一个配置更新时间
    last_update_time = None

    def load_config(self):
        if self.last_update_time is None or time.time() - self.last_update_time > 60:
            # initialization and load config every 1 minutes
            config_values = requests_retry_session().get('your request url').json()
            app_env = 'production or whatever'
            self.do_update(app_env, config_values)

    @classmethod
    def do_update(cls, app_env, config_values):
          # 记得要更新配置的时间
          cls.last_update_time = time.time()
          cls.db_config = config_values.get('db_config')
          ...
           

如上所示, @classmethod声明了函数do_update()为类函数, cls指向该类, 在该函数内设置的变量为类变量, 类变量(attribute)的好处是绑定在class上的, 即使你在程序的各个地方都创建了ConfigReader对象, 只要有一个对象在调用的时候发现超时了需要再次调用do_update, 并更新了诸如db_config等attribute的值, 那么所有ConfigReader对象的attribute db_config都是最新的.

下面提供一段代码仅供理解:

# -*- coding: utf-8 -*-
import time
class ConfigReader(object):
    # 初始化一个配置更新时间
    last_update_time = None

    def load_config(self):
        if self.last_update_time is None or time.time() - self.last_update_time > 6:
            # initialization and load config every 1 minutes
            config_values = dict(db_config="localhost:3306")
            app_env = 'production or whatever'
            print "do load config"
            self.do_update(app_env, config_values)
        else:
            print "not yet to load config"

    @classmethod
    def do_update(cls, app_env, config_values):
          # 记得要更新配置的时间
          cls.last_update_time = time.time()
          cls.db_config = config_values.get('db_config')

config_reader1 = ConfigReader()
config_reader1.load_config()
print "reader1 update time: %s" % config_reader1.last_update_time

config_reader2 = ConfigReader()
config_reader2.load_config()
print "reader2 update time: %s" % config_reader2.last_update_time

print "sleep 6 second, see if it load again..."
time.sleep(6)
config_reader2.load_config()

print "reader1 update time: %s" % config_reader1.last_update_time
print "reader2 update time: %s" % config_reader2.last_update_time

输出结果:

do load config
reader1 update time: 1519571163.71
not yet to load config
reader2 update time: 1519571163.71
sleep 6 second, see if it load again...
do load config
reader1 update time: 1519571169.71
reader2 update time: 1519571169.71

从上述代码的输出结果可见, attribute是跟类绑定的, 即使我创建了config_reader1config_reader2, 不管中间谁执行了load_config()方法, 它们的last_update_time变量是保持一致的.

当然缓存有很多方法, 比如可以写到本地文件, 看文件的最后更新时间来决定是否更新缓存. 像上述的方法, 简单的保存在内存中, 适合数据量不大的情景. 通过以上的可靠获取配置, 有效更新配置的方法, 就能够实现实时地去修改配置项, 无需重启应用啦.

Reference

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容