连接池的实现 redis例子
# -*- encoding:utf-8 -*-
# import pymysql
#
# conn = pymysql.connect(host="127.0.0.1", port=3306, user="py",passwd="", db="codeline",client_flag=5)
# #conn2 = pymysql.connect(host="127.0.0.1", port=3306, user="py",
# # passwd="", db="codeline")
# conn.ping(True)
# #conn2.ping(True)
# while True:
# print (1)
# cursor = conn.cursor()
# SQL = ("SELECT username FROM logint WHERE username='%s'" % username)
# print(username)
# cursor.execute(SQL)
# user = cursor.fetchone()
# conn.commit() # Redis是StrictRedis的子类
class Redis(StrictRedis):
"""
Provides backwards compatibility with older versions of redis-py that
changed arguments to some commands to be more Pythonic, sane, or by
accident.
"""
# StrictRedis基类
class StrictRedis(object):
"""
Implementation of the Redis protocol. This abstract class provides a Python interface to all Redis commands
and an implementation of the Redis protocol. Connection and Pipeline derive from this, implementing how
the commands are sent and received to the Redis server
""" def __init__(self, host='localhost', port=6379,
db=0, password=None, socket_timeout=None,
socket_connect_timeout=None,
socket_keepalive=None, socket_keepalive_options=None,
connection_pool=None, unix_socket_path=None,
encoding='utf-8', encoding_errors='strict',
charset=None, errors=None,
decode_responses=False, retry_on_timeout=False,
ssl=False, ssl_keyfile=None, ssl_certfile=None,
ssl_cert_reqs=None, ssl_ca_certs=None):
# 如果connection_pool没有定义的话就尝试获取其它设置后并重新创建连接池
if not connection_pool:
if charset is not None:
warnings.warn(DeprecationWarning(
'"charset" is deprecated. Use "encoding" instead'))
encoding = charset
if errors is not None:
warnings.warn(DeprecationWarning(
'"errors" is deprecated. Use "encoding_errors" instead'))
encoding_errors = errors kwargs = {
'db': db,
'password': password,
'socket_timeout': socket_timeout,
'encoding': encoding,
'encoding_errors': encoding_errors,
'decode_responses': decode_responses,
'retry_on_timeout': retry_on_timeout
}
# based on input, setup appropriate connection args
if unix_socket_path is not None:
kwargs.update({
'path': unix_socket_path,
'connection_class': UnixDomainSocketConnection
})
else:
# TCP specific options
kwargs.update({
'host': host,
'port': port,
'socket_connect_timeout': socket_connect_timeout,
'socket_keepalive': socket_keepalive,
'socket_keepalive_options': socket_keepalive_options,
}) if ssl:
kwargs.update({
'connection_class': SSLConnection,
'ssl_keyfile': ssl_keyfile,
'ssl_certfile': ssl_certfile,
'ssl_cert_reqs': ssl_cert_reqs,
'ssl_ca_certs': ssl_ca_certs,
})
connection_pool = ConnectionPool(**kwargs)
# 如果没有已经创建连接池则使用已经创建的连接池如果没有连接池则默认也会创建连接池
self.connection_pool = connection_pool
self._use_lua_lock = None self.response_callbacks = self.__class__.RESPONSE_CALLBACKS.copy() # COMMAND EXECUTION AND PROTOCOL PARSING
def execute_command(self, *args, **options):
"Execute a command and return a parsed response"
pool = self.connection_pool
command_name = args[0]
# 从连接池中获取连接执行command_name
connection = pool.get_connection(command_name, **options)
try:
connection.send_command(*args)
return self.parse_response(connection, command_name, **options)
except (ConnectionError, TimeoutError) as e:
connection.disconnect()
if not connection.retry_on_timeout and isinstance(e, TimeoutError):
raise
connection.send_command(*args)
return self.parse_response(connection, command_name, **options)
finally:
pool.release(connection) # 创建连接池类
class ConnectionPool(object):
"Generic connection pool"
@classmethod
def from_url(cls, url, db=None, **kwargs):
"""
Return a connection pool configured from the given URL. For example:: redis://[:password]@localhost:6379/0
rediss://[:password]@localhost:6379/0
unix://[:password]@/path/to/socket.sock?db=0 Three URL schemes are supported:
redis:// creates a normal TCP socket connection
rediss:// creates a SSL wrapped TCP socket connection
unix:// creates a Unix Domain Socket connection There are several ways to specify a database number. The parse function
will return the first specified option:
1. A ``db`` querystring option, e.g. redis://localhost?db=0
2. If using the redis:// scheme, the path argument of the url, e.g.
redis://localhost/0
3. The ``db`` argument to this function. If none of these options are specified, db=0 is used. Any additional querystring arguments and keyword arguments will be
passed along to the ConnectionPool class's initializer. In the case
of conflicting arguments, querystring arguments always win.
""" def __init__(self, connection_class=Connection, max_connections=None,
**connection_kwargs):
"""
Create a connection pool. If max_connections is set, then this
object raises redis.ConnectionError when the pool's limit is reached. By default, TCP connections are created connection_class is specified.
Use redis.UnixDomainSocketConnection for unix sockets. Any additional keyword arguments are passed to the constructor of
connection_class.
"""
# 最大连接数默认为62
max_connections = max_connections or 2 ** 31
if not isinstance(max_connections, (int, long)) or max_connections < 0:
raise ValueError('"max_connections" must be a positive integer') self.connection_class = connection_class
self.connection_kwargs = connection_kwargs
self.max_connections = max_connections
# 初始化线程池
self.reset() def __repr__(self):
return "%s<%s>" % (
type(self).__name__,
self.connection_class.description_format % self.connection_kwargs,
)
# 初始化线程池
def reset(self):
# 获取当前的进程号,后面判断进程是否挂掉
self.pid = os.getpid()
# 存放已经创建的连接(计数)
self._created_connections = 0
# 存放可用的连接对象(列表)
self._available_connections = []
# 存放正在使用的连接对象(集合)
self._in_use_connections = set()
# 创建线程锁
self._check_lock = threading.Lock() def _checkpid(self):
if self.pid != os.getpid():
with self._check_lock:
if self.pid == os.getpid():
# another thread already did the work while we waited
# on the lock.
return
self.disconnect()
self.reset()
# 从连接池中获取连接
def get_connection(self, command_name, *keys, **options):
"Get a connection from the pool"
self._checkpid()
try:
# 从连接池中pop出一个连接对象
connection = self._available_connections.pop()
except IndexError:
# 如果连接池中已经没有连接的话重新创建连接
connection = self.make_connection()
# 当前正在使用的连接集合中添加此连接
self._in_use_connections.add(connection)
# 并返回此连接对象
return connection
# 创建连接
def make_connection(self):
"Create a new connection"
# 如果大于最大连接数则抛出异常
if self._created_connections >= self.max_connections:
raise ConnectionError("Too many connections")
# 否则创建的连接数++
self._created_connections += 1
# 利用创建连接类实例化一个连接
return self.connection_class(**self.connection_kwargs)
# 释放连接
def release(self, connection):
"Releases the connection back to the pool"
self._checkpid()
if connection.pid != self.pid:
return
# 并没有关闭连接而是从在使用的连接列表中删除此连接,回收连接对象
self._in_use_connections.remove(connection)
# 可用连接就回收了一个连接对象
self._available_connections.append(connection)
# 关闭连接
def disconnect(self):
"Disconnects all connections in the pool"
# 关联获取所有可迭代对象获取所有连接对象
all_conns = chain(self._available_connections,
self._in_use_connections)
# 便历关闭所有连接
for connection in all_conns:
connection.disconnect() # 创建连接类
class Connection(object):
"Manages TCP communication to and from a Redis server"
description_format = "Connection<host=%(host)s,port=%(port)s,db=%(db)s>" def __init__(self, host='localhost', port=6379, db=0, password=None,
socket_timeout=None, socket_connect_timeout=None,
socket_keepalive=False, socket_keepalive_options=None,
retry_on_timeout=False, encoding='utf-8',
encoding_errors='strict', decode_responses=False,
parser_class=DefaultParser, socket_read_size=65536):
self.pid = os.getpid()
self.host = host
self.port = int(port)
self.db = db
self.password = password
self.socket_timeout = socket_timeout
self.socket_connect_timeout = socket_connect_timeout or socket_timeout
self.socket_keepalive = socket_keepalive
self.socket_keepalive_options = socket_keepalive_options or {}
self.retry_on_timeout = retry_on_timeout
self.encoding = encoding
self.encoding_errors = encoding_errors
self.decode_responses = decode_responses
self._sock = None
self._parser = parser_class(socket_read_size=socket_read_size)
self._description_args = {
'host': self.host,
'port': self.port,
'db': self.db,
}
self._connect_callbacks = [] def __repr__(self):
return self.description_format % self._description_args
# 对象删除时调用disconnect方法关闭对象
def __del__(self):
try:
self.disconnect()
except Exception:
pass def register_connect_callback(self, callback):
self._connect_callbacks.append(callback) def clear_connect_callbacks(self):
self._connect_callbacks = []
# 核心的连接方法还是通过socket模块儿实现
def connect(self):
"Connects to the Redis server if not already connected"
if self._sock:
return
try:
# 私有方法创建连接
sock = self._connect()
except socket.error:
e = sys.exc_info()[1]
raise ConnectionError(self._error_message(e)) self._sock = sock
try:
self.on_connect()
except RedisError:
# clean up after any error in on_connect
self.disconnect()
raise # run any user callbacks. right now the only internal callback
# is for pubsub channel/pattern resubscription
for callback in self._connect_callbacks:
callback(self)
# 创建tcp socket
def _connect(self):
"Create a TCP socket connection"
# we want to mimic what socket.create_connection does to support
# ipv4/ipv6, but we want to set options prior to calling
# socket.connect()
err = None
for res in socket.getaddrinfo(self.host, self.port, 0,
socket.SOCK_STREAM):
family, socktype, proto, canonname, socket_address = res
sock = None
try:
sock = socket.socket(family, socktype, proto)
# TCP_NODELAY
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # TCP_KEEPALIVE
# 默认使用的是短连接,设置socket_keepalive=True保持长连接
if self.socket_keepalive:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
for k, v in iteritems(self.socket_keepalive_options):
sock.setsockopt(socket.SOL_TCP, k, v)
# 设置超市时间,默认不设置为阻塞模式
# set the socket_connect_timeout before we connect
sock.settimeout(self.socket_connect_timeout) # connect
sock.connect(socket_address) # set the socket_timeout now that we're connected
sock.settimeout(self.socket_timeout)
return sock except socket.error as _:
err = _
if sock is not None:
sock.close() if err is not None:
raise err
raise socket.error("socket.getaddrinfo returned an empty list") def _error_message(self, exception):
# args for socket.error can either be (errno, "message")
# or just "message"
if len(exception.args) == 1:
return "Error connecting to %s:%s. %s." % \
(self.host, self.port, exception.args[0])
else:
return "Error %s connecting to %s:%s. %s." % \
(exception.args[0], self.host, self.port, exception.args[1]) def on_connect(self):
"Initialize the connection, authenticate and select a database"
self._parser.on_connect(self) # if a password is specified, authenticate
if self.password:
self.send_command('AUTH', self.password)
if nativestr(self.read_response()) != 'OK':
raise AuthenticationError('Invalid Password') # if a database is specified, switch to it
if self.db:
self.send_command('SELECT', self.db)
if nativestr(self.read_response()) != 'OK':
raise ConnectionError('Invalid Database')
# 关闭连接
def disconnect(self):
"Disconnects from the Redis server"
self._parser.on_disconnect()
if self._sock is None:
return
try:
# 先shutdown然后再close
self._sock.shutdown(socket.SHUT_RDWR)
self._sock.close()
except socket.error:
pass
self._sock = None def send_packed_command(self, command):
"Send an already packed command to the Redis server"
if not self._sock:
self.connect()
try:
if isinstance(command, str):
command = [command]
for item in command:
self._sock.sendall(item)
except socket.timeout:
self.disconnect()
raise TimeoutError("Timeout writing to socket")
except socket.error:
e = sys.exc_info()[1]
self.disconnect()
if len(e.args) == 1:
_errno, errmsg = 'UNKNOWN', e.args[0]
else:
_errno, errmsg = e.args
raise ConnectionError("Error %s while writing to socket. %s." %
(_errno, errmsg))
except:
self.disconnect()
raise def send_command(self, *args):
"Pack and send a command to the Redis server"
self.send_packed_command(self.pack_command(*args)) def can_read(self):
"Poll the socket to see if there's data that can be read."
sock = self._sock
if not sock:
self.connect()
sock = self._sock
return bool(select([sock], [], [], 0)[0]) or self._parser.can_read() def read_response(self):
"Read the response from a previously sent command"
try:
response = self._parser.read_response()
except:
self.disconnect()
raise
if isinstance(response, ResponseError):
raise response
return response def encode(self, value):
"Return a bytestring representation of the value"
if isinstance(value, Token):
return b(value.value)
elif isinstance(value, bytes):
return value
elif isinstance(value, (int, long)):
value = b(str(value))
elif isinstance(value, float):
value = b(repr(value))
elif not isinstance(value, basestring):
value = str(value)
if isinstance(value, unicode):
value = value.encode(self.encoding, self.encoding_errors)
return value def pack_command(self, *args):
"Pack a series of arguments into the Redis protocol"
output = []
# the client might have included 1 or more literal arguments in
# the command name, e.g., 'CONFIG GET'. The Redis server expects these
# arguments to be sent separately, so split the first argument
# manually. All of these arguements get wrapped in the Token class
# to prevent them from being encoded.
command = args[0]
if ' ' in command:
args = tuple([Token(s) for s in command.split(' ')]) + args[1:]
else:
args = (Token(command),) + args[1:] buff = SYM_EMPTY.join(
(SYM_STAR, b(str(len(args))), SYM_CRLF)) for arg in imap(self.encode, args):
# to avoid large string mallocs, chunk the command into the
# output list if we're sending large values
if len(buff) > 6000 or len(arg) > 6000:
buff = SYM_EMPTY.join(
(buff, SYM_DOLLAR, b(str(len(arg))), SYM_CRLF))
output.append(buff)
output.append(arg)
buff = SYM_CRLF
else:
buff = SYM_EMPTY.join((buff, SYM_DOLLAR, b(str(len(arg))),
SYM_CRLF, arg, SYM_CRLF))
output.append(buff)
return output def pack_commands(self, commands):
"Pack multiple commands into the Redis protocol"
output = []
pieces = []
buffer_length = 0 for cmd in commands:
for chunk in self.pack_command(*cmd):
pieces.append(chunk)
buffer_length += len(chunk) if buffer_length > 6000:
output.append(SYM_EMPTY.join(pieces))
buffer_length = 0
pieces = [] if pieces:
output.append(SYM_EMPTY.join(pieces))
return output
连接池的实现 redis例子的更多相关文章
- jedis连接池详解(Redis)
转自:http://tianxingzhe.blog.51cto.com/3390077/1684306 原子性(atomicity): 一个事务是一个不可分割的最小工作单位,事务中包括的诸操作要么都 ...
- 压测过程中,获取不到redis连接池,发现redis连接数高
说明:图片截得比较大,浏览器放大倍数看即可(涉及到隐私,打了码,请见谅,如果有疑问,欢迎骚扰). 最近在压测过程中,出现获取不到redis连接池的问题 xshell连接redis服务器,查看连接数,发 ...
- Jedis连接池对Redis数据操作
[效果图] [前言] Redis是常用于缓存的非关系型数据库,感觉更像加强型的HashMap的用法,依靠Key和Value保存数据.官方推荐用Jedis来操作Redis数据库,使用和JDBC差不多,一 ...
- 泊爷带你学go -- redis连接池的操作
package main import ( "common" "fmt" "proto" "strconv" " ...
- OpenResty 高阶实战之————Redis授权登录使用短连接(5000)和长连接(500W) 使用连接池AB压力测试结果
一.短连接开始测试 ab -n 5000 -c 100 -k 127.0.0.1/test_redis_short #demo1 Concurrency Level: Time taken for t ...
- python 基础 10.0 nosql 简介--redis 连接池及管道
一. NOSQL 数据库简介 NoSQL 泛指非关系型的数据库.非关系型数据库与关系型数据库的差别 非关系型数据库的优势: 1.性能NOSQL 是基于键值对的,可以想象成表中的主键和值的对应关系,而且 ...
- Spring整合Redis,并配置Jedis连接池
目录 只言片语 创建redis连接池的配置文件 单机版 spring整合redis(使用JedisPool) 项目中使用示例 集群版 spring整合redis(使用JedisCluster) 项目中 ...
- redis连接池 go-redis
为什么使用连接池? 首先Redis也是一种数据库,它基于C/S模式,因此如果需要使用必须建立连接,稍微熟悉网络的人应该都清楚地知道为什么需要建立连接,C/S模式本身就是一种远程通信的交互模式,因此Re ...
- jedis的连接池
1.需要先打开虚拟机,并开启Linux系统的端口号:6379: 其中,第一行代码为修改字符编码格式,解决SSH中文乱码问题. 2.开启redis: 3.利用连接池实现数据的存取: (1)代码实现: i ...
随机推荐
- Rocksdb引擎记录格式
Rocksdb是一个kv引擎,由facebook团队基于levelDB改进而来,Rocksdb采用LSM-tree存储数据,良好的读写特性以及压缩特性使得其非常受欢迎.此外,Rocksdb引擎作为插件 ...
- 如何监控ORACLE索引使用与否
在数据库管理与维护中,我们总会遇到一个问题:我们创建的索引是否会被某些SQL语句使用呢?换个通俗表达方式:我创建的索引是否是未使用的索引(unused Indexes),是否有价值呢?如果创建的某个索 ...
- Oracle学习笔记四 SQL命令(二):SQL操作语言类别
SQL分为下列语言类别 1.数据定义语言(DDL) Create.Alter.Drop 2.数据操纵语言(DML) Insert.Select.Delete.Update 3.事务控制语言(TCL) ...
- python-open文件处理
python内置函数open()用于打开文件和创建文件对象 语法 open(name[,mode[,bufsize]]) name:文件名 mode:指定文件的打开模式 r:只读 w:写入 a:附加 ...
- Python来做应用题及思路
Python来做应用题及思路 最近找工作头疼没事就开始琢磨python解应用题应该可以,顺便还可以整理下思路当然下面的解法只是个人理解,也欢迎大佬们给意见或者指点更好的解决办法等于优化代码了嘛,也欢迎 ...
- Ubuntu15.04安装不完全指南
0x00. 烧盘 使用UltraISO(破解版)烧录到U盘里,设置电脑从U盘启动,即可安装. 安装时可能出现not COM32R image的命令行,“boot:” 后面直接输入live即可解决问题. ...
- 解决ubuntu eclipse中 Android SDK Manager 图标不见的方法
在eclipse中选择的步骤如下: Window ---> Custom Perspective --> command Group Availability ----> an ...
- mysql数据库主从及主主复制配置演示
实验系统:CentOS 6.6_x86_64 实验前提:提前准备好编译环境,防火墙和selinux都关闭 实验说明:本实验共有2台主机,IP分配如拓扑 实验软件:mariadb-10.0.20 实验拓 ...
- 基于pcDuino-V2的无线视频智能小车
这段时间抽空做了个智能视频小车.包含了pid电机控制.socket网络编程.多线程编程.epoll机制.gtk图形界面编程. 这是界面: 小车的底层是用的stm32f405系列的单片机+电机驱动做的一 ...
- [No0000A4]DOS命令(cmd)批处理:替换字符串、截取字符串、扩充字符串、获取字符串长度
1.替换字符串,即将某一字符串中的特定字符或字符串替换为给定的字符串.举例说明其功能:========================================= @echo off set a ...