OpenStack中memcached的使用和實現

概述

主要分享下我的對Liberty版本openstack中cache使用的理解,因爲做者水平有限,不免有所錯誤,疏漏,還望批評指正。python

openstack中可使用cache層來緩存數據,Liberty版本主要有如下幾種場景:redis

  • 存儲函數執行結果 keystone heat nova等項目把一些固定的屬性和查詢請求的結果放到cache裏面,加速訪問。
  • 存儲keystone token token建立完成以後,不須要修改,會有大量的讀操做,適合放到cache中
  • 存儲keystonemiddleware token 爲neutron,cinder,nova等各個項目緩存從keystone得到的token。
  • 存儲horizon用戶會話數據 主要是django支持使用

存儲函數執行結果

優點在於,不少查詢函數的結果是固定的,可是又比較經常使用,查詢一次以後,按照key-value存到cache中,再次查詢時不須要訪問數據庫,直接從內存緩存中根據key將結果取出,能夠提升不少速度。或者還有一些查詢請求,查詢的參數變幻無窮,可是結果只有固定的幾類,這種更適合使用cache加速。sql

Liberty 版本openstack主要是nova和keystone這方面用的比較多,來看nova中的一個例子:數據庫

def get_instance_availability_zone(context, instance):
    """Return availability zone of specified instance."""
    host = instance.get('host')
    if not host:
        # 若是虛擬機尚未被分配到主機上,就把建立虛擬機時指定的zone信息取出返回
        az = instance.get('availability_zone')
        return az

    #虛擬機所在的zone取決於虛擬機所在物理機的歸屬的zone,因此能夠生成一個
    #'azcache-主機名'樣式的字符串做爲cache key      
    cache_key = _make_cache_key(host)
    #獲取一個鏈接cache的client
    cache = _get_cache()
    #嘗試取出這個key在cache中的值,做爲zone信息,可能爲空,也可能直接是結果
    az = cache.get(cache_key)
    #取出實例對象中的availability_zone屬性
    az_inst = instance.get('availability_zone')
    if az_inst is not None and az != az_inst:
        #對象屬性中有zone信息,且和cache取到的zone信息不一致,那麼須要從新獲取zone信息
        #而且更新到cache中
        az = None
    if not az:
        #若是cache中沒有取到zone信息,或者是在上一步中zone信息被清空,從新獲取zone信息
        elevated = context.elevated()
        az = get_host_availability_zone(elevated, host)
        #更新cache
        cache.set(cache_key, az)
    #返回zone信息
    return az

這樣除第一次查詢到這臺物理機上的虛擬機外,查詢這臺物理機上的任何虛擬機所屬的zone,不再須要訪問數據庫,這樣極大地減小了對數據庫的請求數量,提升了響應速度。
這裏支持的cache後端包括memcached,redis,mongondb或者是python的dict.目前主流openstack發行版推薦的選項是memcached,簡單穩定,性能和功可以用。django

存儲keystone token

keystone中除了Fernet格式的token外,其餘格式的token都須要keystone由存儲起來,存儲支持如下幾種driver:後端

  • sql 將token存放在數據庫中,使用這種方法須要按期清理過時token,防止token表太大影響性能。
  • memcache 將token存放到memcache中,token自己建立以後不會被修改,只會被讀,因此很適合放到cache中,加速訪問,放到cache中也不須要寫腳本按期清理數據庫中的過時token。
  • memcache_pool 在memcache的基礎上,實現了memcache的鏈接池,能夠在線程之間複用鏈接。

一個常見的memcache token driver的配置多是這樣的:api

[token]
caching = true
provider = keystone.token.providers.uuid.Provider
driver = keystone.token.persistence.backends.memcache.Token
[memcache]
servers = controller-1:11211,controller-2:11211,controller-3:11211

memcache driver的缺點在於,memcache自己是分佈式的設計,可是並非高可用的,若是controller-1上的的cache服務被重啓,這個節點上的全部token都會丟失掉,會帶來一些錯誤。緩存

比這更糟糕的是,若是controller1網絡不可達或者宕機,那麼咱們會觀察到幾乎每一個openstack api請求都會有3s以上的卡頓。這是由於openstack默認使用python-memcached訪問memcache,它提供的操做keystone的client繼承自Thread.local類,和構建它的線程綁定。openstack服務啓動後,會啓動必定數量的子進程,每一個http request到達,會有一個子進程接收,孵化一個線程去處理這個請求。若是用到memcache,線程會調用python-memcached
構建一個client類,經過這個client的實例對memcache進行操做。若是訪問到網絡不可達的memcache節點,卡住,操做超時,將這個節點標識爲30秒內不可用,在這個線程內,不會再所以而卡住,可是這個http請求結束以後,下一個http請求過來,從新孵化的線程會reinit這個client,新的client丟失了舊的client的狀態,仍是可能會訪問到卡住的memcache節點上。網絡

社區之因此要作memcache_pool,就是爲了解決這個問題,將client統一由pool管理起來,memcache節點的狀態,也由pool管理起來,這樣每一個子進程裏只會卡第一次,因此強烈推薦使用memcache_pool驅動而不是memcache。社區將memcache_pool的代碼從keystone複製到oslo_cache項目中,但願全部使用memcache的項目都經過它的memcachepool去訪問,避免這個問題。其中,nova在M版本支持,heat在L版本支持。dom

具體的各個服務如何配置使用memcache_pool driver這裏再也不贅述。

存儲keystonemiddleware token

咱們請求任何openstack服務時,該服務都要校驗請求中提供的token是否合理,這部分代碼顯然不是每一個項目都本身實現一遍,它在keystonemiddleware項目實現,並做爲filter配置在各個項目的api-paste.ini文件中,以下所示:

[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory

當請求到達服務時,由keystonemiddleware訪問keystone來查詢請求攜帶的token是否合法,一般咱們一個token會使用不少次,因此keystonemiddleware建議使用memcache緩存,把從keystone取到的token緩存一段時間,默認是300秒,以減小對keystone的壓力,提升性能。kolla項目中nova keystonemiddleware配置示例以下:

[keystone_authtoken]
[keystone_authtoken]
auth_uri = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_public_port }}
auth_url = {{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_admin_port }}
auth_plugin = password
project_domain_id = default
user_domain_id = default
project_name = service
username = {{ nova_keystone_user }}
password = {{ nova_keystone_password }}

memcache_security_strategy = ENCRYPT
memcache_secret_key = {{ memcache_secret_key }}
memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}

一直以來,若是不配置其中的memcached_servers的的話,每一個服務每一個子進程都會建立一個字典做爲cache存放從keystone得到的token,可是字典內容都是類似的,若是使用統一的memcache,就不會有這方面的問題。如今master版本keystonemiddleware認爲這是不合適的,會致使不一致的結果和較高的內存佔用,計劃在N或者O版本移除這個特性。

存儲horizon用戶會話數據

這個是django支持的功能,這裏不做討論,有興趣的能夠看The Django Book 2.0--中文版

總結

cache相關的具體的配置項能夠參考kolla項目中的cache配置,應該仍是準確合適的。

相關文章
相關標籤/搜索