项目中读取redis的类以及开发(测试)环境的处理**

import redis
from .config import Config

class IndexTool(object):
def __init__(self, host, port, pwd, db):
self.ttl = 7*24*60*60
self.pool = redis.ConnectionPool(
host=host,
port=port,
password=pwd,
db=db,
decode_responses=True
)
self.debug = False

def _get_conn(self):
conn = redis.Redis(connection_pool=self.pool, decode_responses=True)
return conn

@retry.retry(3)
def get(self, key, field):
if self.debug:
key = key + '@debug'
conn = self._get_conn()
val = conn.hget(key, field)
return val

@retry.retry(3)
def get_all(self, key):
if self.debug:
key = key + '@debug'
conn = self._get_conn()
val = conn.hgetall(key)
return val

@retry.retry(3)
def set(self, key, field, val):
if self.debug:
key = key + '@debug'
conn = self._get_conn()
ret = conn.hset(key, field, str(val))
conn.hset(key, 'last_modify', str(time.time()))
conn.expire(key, self.ttl)
return ret

@retry.retry(3)
def hdel(self, key, field):
if self.debug:
key = key + '@debug'
conn = self._get_conn()
ret = conn.hdel(key, field)
return ret

@retry.retry(3)
def set_str(self, key, val):
if self.debug:
key = key + '@debug'
conn = self._get_conn()
ret = conn.set(key, val)
conn.expire(key, self.ttl)
return

这里我们要注意以下几点:

1. redis不适合存放大value和大key,原因如下:
1.内存不均:单value较大时,可能会导致节点之间的内存使用不均匀,间接地影响key的部分和负载不均匀;
2.阻塞请求:redis为单线程,单value较大读写需要较长的处理时间,会阻塞后续的请求处理;
3.阻塞网络:单value较大时会占用服务器网卡较多带宽,可能会影响该服务器上的其他Redis实例或者应用。
因此hash数据结构的value不适合存放json

2. redis的hash数据结构不能单独为某个feild设置ttl,只能为key设置ttl
redis的hash数据结构可以删除某个field,api为hdel(key, feild)

3. redis的hash数据结构尽可能的不要使用hgetall,因为许多redis的hash结构里会有update_time等相关字段,如果逻辑里没有处理掉这些key,极有可能程序会挂。

4. 根据业务经验,redis的key一般会选择md5规范后的字符串

一般来说我们在这个redis的工具类脚本里根据业务逻辑写一些业务工具类去继承redis的基类,比如说

class OfflineIndexTool(IndexTool):
def __init__(self):
conf = Config['offline.index.db']
super(OfflineIndexTool, self).__init__(**conf)

def set(self, key, field, val):
raise RuntimeError('IndexReadOnly')

比如说我们有一个去重的功能

class QcmsPushTool(IndexTool):
def __init__(self):
conf = Config['qcms.push.db']
super(QcmsPushTool, self).__init__(**conf)
self.debug = Config["debug"]

def delete_duplicate(self, key):
res = self.get('conf_uniq', key)
if not res:
self.set('conf_uniq', key, str(time.time()))
return None
else:
if int(time.time()) - int(float(res)) > 86400 * 7:
self.hdel('conf_uniq', key)
self.set('conf_uniq', key, str(time.time()))
return None
else:
return

这段代码有点像LRU,如果key不在redis里或者当前时间超过redis中的存放时间一周,那么将其插入到redis中,返回None;如果key在redis中,那么返回redis中这条记录存放的时间。

目录结构

配置文件​​config.py​​​ 与redis基类同一级目录,​​config.py​​应该写成以下。

#!/usr/bin/env python
# encoding: utf-8
from utils.misc_tool import MiscTool

is_debug = True
if MiscTool.is_online():
is_debug = False

Config = {
'debug': is_debug,
'hotnews.index.db': {
'host': '10.175.162.16',
'port': '26739',
'db': 0,
'pwd': '0296f0b83193cd46360'
},
'offline.index.db': {
'host': '10.175.162.34',
'port': '8648',
'db': 0,
'pwd': '3fe885fb4cec4dcd'
},
'qcms.push.db': {
'host': '10.174.46.229',
'port': '6264',
'db': 0,
'pwd': '716119d74f7c7c9d'
},
'tangshan.push.db': {
'host': '10.174.46.229',
'port': '6264',
'db': 0,
'pwd': '716119d74f7c7c9d'
},
'kuai.news.es': {
'hosts': [{'host': '10.175.162.34', 'port': 18013}],
'timeout': 3600,
'http_auth': 'hvpxs_w:e83330917f35f17af93588f39c08dfcc'
}
}

我们这里的配置文件就是一个大字典(dict)

如何判断是否为线上环境

我们可以写一个基础类,获取当前是否是线上环境还是debug环境

#!/usr/bin/env python
# encoding: utf-8
import hashlib
import os


class MiscTool(object):
@classmethod
def make_md5(cls, input):
md5_obj = hashlib.md5()
md5_obj.update(input.encode())
md5_str = md5_obj.hexdigest()
return md5_str

@classmethod
def is_online(cls):
ret = False
env = os.environ.get('ONLINE')
if env == 'true':
ret = True
return

我们可以通过启动程序的shell脚本来控制当前是debug模式还是线上模式

export ONLINE="true"