RabbitMQ

RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统。

MQ全称Message Queue,消息队列(MQ)是一种应用程序对应用程序的通信方式。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。

 

安装python RabbitMQ 模块

  1. pip install pika
  2. or
  3. easy_install pika

实现一一个简单的队列通信

send端:

  1. import pika
  2. #与RabbitMQ server建立连接
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. 'localhost')) #服务器地址
  5. channel = connection.channel()
  6.  
  7. #声明queue队列以向其发送消息信息,queue=’hello’ 指定队列名字
  8. channel.queue_declare(queue='hello')
  9.  
  10. #message不能直接发送给queue,需要exchange到达queue,此处使用以空字符串的默认的exchage
  1. #使用默认exchange时允许荣光routing_key明确指定message被发送给哪个queue
  1. #body参数指定了要发送的message内存
  2. channel.basic_publish(exchange='',
  3. routing_key='hello',
  4. body='Hello World!')
  5. print(" [x] Sent 'Hello World!'")
  6. #关闭与RabbitMQ server间的连接
  1. connection.close()

receive端:

  1. import pika
  2. #建立到达RabbitMQ server的连接
  1. #这里RabbitMQ 位于本机 localhost
  2. connection = pika.BlockingConnection(pika.ConnectionParameters(
  3. 'localhost'))
  4. channel = connection.channel()
  5.  
  6. #声明queue,确认要从中接收message的queue
  1. # queue_declare 函数 可以运行多次,但只会创建一次
  1. #若可以确认queue是已经存在的,则这里可以省略,但是要习惯这里重新声明
  2. channel.queue_declare(queue='hello')
  3. # 定义回调函数
  1. #一旦从queue中接收到一个消息回调函数将被调用
  2. def callback(ch, method, properties, body):
  3. print(" [x] Received %r" % body)
  4. # 从queue接收消息的设置
  1. #包括重哪个queue接收消息,用于处理消息的callback,是否要确认消息
  1. #默认情况下要对消息进行确认,以防消息丢失
  1. #此处 no_ack=True ,不对消息进行确认
  2. channel.basic_consume(callback,
  3. queue='hello',
  4. no_ack=True)
  5.  
  6. print(' [*] Waiting for messages. To exit press CTRL+C')
  7. # 开始循环从queue中接收消息,并使用回调函数进行处理
  1. channel.start_consuming()

 

Work Queues

在这种模式下,RabbitMQ会默认把p发的消息依次分发给各个消费者(C),有点类似轮询

P端代码:

  1. import pika
  2.  
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. 'localhost'))
  5. channel = connection.channel()
  6.  
  7. #声明queue
  8. channel.queue_declare(queue='task_queue')
  9.  
  10. import sys
  11. # 从命令行构造将要发送的消息
  12. message = ' '.join(sys.argv[1:]) or "Hello World!"
  1. #除了要声明queue是持久化的外,还要生命消息是持久化的
  1. #basic_publish的properties参数指定的消息属性
  1. #此处pika.BasicProperties中的delivery_mode = 2 指明消息为持久化
  1. channel.basic_publish(exchange='',
  2. routing_key='task_queue',
  3. body=message,
  4. properties=pika.BasicProperties(
  5. delivery_mode = 2,
  6. ))
  7. print(" [x] Sent %r" % message)
  8. connection.close()

C端代码:

  1. import pika,time
  2. #默认情况RabbitMQ 将消息以round-robin方式发送给下一个consumer
  1. #每个consumer接收到的平均 消息量是一样的
  2. connection = pika.BlockingConnection(pika.ConnectionParameters(
  3. 'localhost'))
  4. channel = connection.channel()
  5.  
  6. def callback(ch, method, properties, body):
  7. print(" [x] Received %r" % body)
  8. time.sleep(body.count(b'.'))
  9. print(" [x] Done")
  10. ch.basic_ack(delivery_tag = method.delivery_tag)
  11.  
  12. channel.basic_consume(callback,
  13. queue='task_queue',
  14. )
  15.  
  16. print(' [*] Waiting for messages. To exit press CTRL+C')
  17. channel.start_consuming()

 

消息持久化

在声明Queue时使用queue_declare,添加durable=True,代表着消息被持久化了,就是RabbitMQ重启后queue仍然存在

#仅仅对message进行确认不能保证消息,不丢失,比如RabbitMQ 崩溃了queue就会丢失

  1. channel.queue_declare(queue='hello', durable=True)

消息公平分发

类似负载均衡

如果Rabbit按顺序把消息发送到各个消费者,不考虑负载情况的话,有可能出现配置不高的消费者堆积了很多消息处理不完,配置高的消费着一直清闲。为了解决这个问题,可以再过各个消费者端,配置 perfetch=1,意思就是告诉RabbitMQ在这个消费者还没有处理完之前不再接收新的消息。

  1. channel.basic_qos(prefetch_count=1)

 

带消息持久化和公平分发的完整代码

生产者:

  1. import pika
  2. import sys
  3.  
  4. connection = pika.BlockingConnection(pika.ConnectionParameters(
  5. host='localhost'))
  6. channel = connection.channel()
  7.  
  8. channel.queue_declare(queue='task_queue', durable=True)
  9.  
  10. message = ' '.join(sys.argv[1:]) or "Hello World!"
  11. channel.basic_publish(exchange='',
  12. routing_key='task_queue',
  13. body=message,
  14. properties=pika.BasicProperties(
  15. delivery_mode = 2, # make message persistent
  16. ))
  17. print(" [x] Sent %r" % message)
  18. connection.close()

消费者:

  1. import pika
  2. import time
  3.  
  4. connection = pika.BlockingConnection(pika.ConnectionParameters(
  5. host='localhost'))
  6. channel = connection.channel()
  7.  
  8. channel.queue_declare(queue='task_queue', durable=True)
  9. print(' [*] Waiting for messages. To exit press CTRL+C')
  10.  
  11. def callback(ch, method, properties, body):
  12. print(" [x] Received %r" % body)
  13. time.sleep(body.count(b'.'))
  14. print(" [x] Done")
  15. ch.basic_ack(delivery_tag = method.delivery_tag)
  16.  
  17. channel.basic_qos(prefetch_count=1)
  18. channel.basic_consume(callback,
  19. queue='task_queue')
  20.  
  21. channel.start_consuming()

Publish、Subscribe(消息发布,订阅)

之前的例子都是1对1的消息发送和接收,即消息只能发送到指定的queue里,但有时候想让queue有类似广播的效果,这时候就要用到exchange。

exchange在定义的时候是有类型的,以决定到底是哪些queue符合条件,可以接收消息

fanout:所有bind到exchange的queue都可以接收到消息

direct:通过routingKey和exchange 决定的那个唯一的queue可以接收消息

topic:所有符合routingKey(此时可以是一个表达)的bind的queue可以接收消息

表达式符号说明:#代表一个或多个字符,* 代表任何字符

使用RoutingKey为#,Exchange Type为topic的时候相当于使用fanout

headers:通过headers来决定把消息发给哪些queue

例子:

消息Publisher:

  1. import pika
  2. import sys
  3.  
  4. connection = pika.BlockingConnection(pika.ConnectionParameters(
  5. host='localhost'))
  6. channel = connection.channel()
  7.  
  8. channel.exchange_declare(exchange='logs',
  9. type='fanout')
  10.  
  11. message = ' '.join(sys.argv[1:]) or "info: Hello World!"
  12. channel.basic_publish(exchange='logs',
  13. routing_key='',
  14. body=message)
  15. print(" [x] Sent %r" % message)
  16. connection.close()

消费Subscriber:

  1. import pika
  2.  
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. host='localhost'))
  5. channel = connection.channel()
  6.  
  7. channel.exchange_declare(exchange='logs',
  8. type='fanout')
  9.  
  10. result = channel.queue_declare(exclusive=True) #不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
  11. queue_name = result.method.queue
  12.  
  13. channel.queue_bind(exchange='logs',
  14. queue=queue_name)
  15.  
  16. print(' [*] Waiting for logs. To exit press CTRL+C')
  17.  
  18. def callback(ch, method, properties, body):
  19. print(" [x] %r" % body)
  20.  
  21. channel.basic_consume(callback,
  22. queue=queue_name,
  23. no_ack=True)
  24.  
  25. channel.start_consuming()

 

有选择的接收消息(exchange type=direct)

RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据关键字判断应该将数据发送至指定队列。

publisher:

  1. import pika
  2. import sys
  3.  
  4. connection = pika.BlockingConnection(pika.ConnectionParameters(
  5. host='localhost'))
  6. channel = connection.channel()
  7.  
  8. channel.exchange_declare(exchange='direct_logs',
  9. type='direct')
  10.  
  11. severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
  12. message = ' '.join(sys.argv[2:]) or 'Hello World!'
  13. channel.basic_publish(exchange='direct_logs',
  14. routing_key=severity,
  15. body=message)
  16. print(" [x] Sent %r:%r" % (severity, message))
  17. connection.close()

Subscriber:

  1. import pika
  2. import sys
  3.  
  4. connection = pika.BlockingConnection(pika.ConnectionParameters(
  5. host='localhost'))
  6. channel = connection.channel()
  7.  
  8. channel.exchange_declare(exchange='direct_logs',
  9. type='direct')
  10.  
  11. result = channel.queue_declare(exclusive=True)
  12. queue_name = result.method.queue
  13.  
  14. severities = sys.argv[1:]
  15. if not severities:
  16. sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
  17. sys.exit(1)
  18.  
  19. for severity in severities:
  20. channel.queue_bind(exchange='direct_logs',
  21. queue=queue_name,
  22. routing_key=severity)
  23.  
  24. print(' [*] Waiting for logs. To exit press CTRL+C')
  25.  
  26. def callback(ch, method, properties, body):
  27. print(" [x] %r:%r" % (method.routing_key, body))
  28. channel.basic_consume(callback,
  29. queue=queue_name,
  30. no_ack=True)
  31.  
  32. channel.start_consuming()

### no_ack=False,如果生产者遇到情况挂掉,那么RabbitMQ会重新将该任务添加到队列中。

更细致的消息过滤

pubisher

  1. import pika
  2. import sys
  3.  
  4. connection = pika.BlockingConnection(pika.ConnectionParameters(
  5. host='localhost'))
  6. channel = connection.channel()
  7.  
  8. channel.exchange_declare(exchange='topic_logs',
  9. type='topic')
  10.  
  11. routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
  12. message = ' '.join(sys.argv[2:]) or 'Hello World!'
  13. channel.basic_publish(exchange='topic_logs',
  14. routing_key=routing_key,
  15. body=message)
  16. print(" [x] Sent %r:%r" % (routing_key, message))
  17. connection.close()

Subscriber

  1. import pika
  2. import sys
  3.  
  4. connection = pika.BlockingConnection(pika.ConnectionParameters(
  5. host='localhost'))
  6. channel = connection.channel()
  7.  
  8. channel.exchange_declare(exchange='topic_logs',
  9. type='topic')
  10.  
  11. result = channel.queue_declare(exclusive=True)
  12. queue_name = result.method.queue
  13.  
  14. binding_keys = sys.argv[1:]
  15. if not binding_keys:
  16. sys.stderr.write("Usage: %s [binding_key]...\n" % sys.argv[0])
  17. sys.exit(1)
  18.  
  19. for binding_key in binding_keys:
  20. channel.queue_bind(exchange='topic_logs',
  21. queue=queue_name,
  22. routing_key=binding_key)
  23.  
  24. print(' [*] Waiting for logs. To exit press CTRL+C')
  25.  
  26. def callback(ch, method, properties, body):
  27. print(" [x] %r:%r" % (method.routing_key, body))
  28.  
  29. channel.basic_consume(callback,
  30. queue=queue_name,
  31. no_ack=True)
  32. channel.start_consuming()

 

Remote procedure call(RPC)

  1. fibonacci_rpc = FibonacciRpcClient()
  2. result = fibonacci_rpc.call(4)
  3. print("fib(4) is %r" % result)

 

RPC server

  1. import pika
  2. import time
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. host='localhost'))
  5.  
  6. channel = connection.channel()
  7.  
  8. channel.queue_declare(queue='rpc_queue')
  9.  
  10. def fib(n):
  11. if n == 0:
  12. return 0
  13. elif n == 1:
  14. return 1
  15. else:
  16. return fib(n-1) + fib(n-2)
  17. # 回调函数,从queue接收到消息后调用该函数进行处理
  18. def on_request(ch, method, props, body):
  19. n = int(body)
  20.  
  21. print(" [.] fib(%s)" % n)
  22. response = fib(n)
  23. # exchange 为空字符串则将消息发送给routing_key指定的queue
  1. # 这里queue为回调函数参数的props中reply_ro指定的queue
  1. # 要发送的消息为计算所地的背波那契数
  1. # properties 中 correlation_id 只定为回调函数参数props中的的correlation_id
  1. # 最后对消息进行确认
  2. ch.basic_publish(exchange='',
  3. routing_key=props.reply_to,
  4. properties=pika.BasicProperties(correlation_id = \
  5. props.correlation_id),
  6. body=str(response))
  7. ch.basic_ack(delivery_tag = method.delivery_tag)
  8. # 只有consumer已经处理并确认了上一条消息时queue才会派发新的消息
  9. channel.basic_qos(prefetch_count=1)
  1. # 设置consumeer参数,即从哪个queue获取消息使用哪个函数进行处理,是否对消息进行确认
  2. channel.basic_consume(on_request, queue='rpc_queue')
  3.  
  4. print(" [x] Awaiting RPC requests")
  5. # 开始接收并处理消息
  1. channel.start_consuming()

RPC client

  1. import pika
  2. import uuid
  3. # 在一个类中封装了connection建立,queue声明,consumer配置,回调函数等
  4. class FibonacciRpcClient(object):
  5. def __init__(self):
  1. # 建立到RabbitMQ的connection
  2. self.connection = pika.BlockingConnection(pika.ConnectionParameters(
  3. host='localhost'))
  4.  
  5. self.channel = self.connection.channel()
  6. # 声明一个临时的回调队列
  7. result = self.channel.queue_declare(exclusive=True)
  8. self.callback_queue = result.method.queue
  9. #此处clinet既是producer又是consumer,因此要配置consumer参数
  1. # 这里的指明从client自己创建的临时队列中接收消息
  1. # 并使用on_response函数处理消息
  1. #不对消息进行确认
  2. self.channel.basic_consume(self.on_response, no_ack=True,
  3. queue=self.callback_queue)
  4. # 定义回调函数
  1. # 比较类的corr_id属性与props中corr_id属性的值
  1. # 若相同则response属性为接收到的消息
  2. def on_response(self, ch, method, props, body):
  3. if self.corr_id == props.correlation_id:
  4. self.response = body
  5.  
  6. def call(self, n):
  1. # 初始化response 和corr_id属性
  2. self.response = None
  3. self.corr_id = str(uuid.uuid4())
  1. # 使用默认exchange向server中定义的rpc_queue发送消息
  1. # 在properties中指定replay_to属性和correlation_id属性用于告知远程server
  1. # correlation_id属性用于匹配 request和response
  2. self.channel.basic_publish(exchange='',
  3. routing_key='rpc_queue',
  4. properties=pika.BasicProperties(
  5. reply_to = self.callback_queue,
  6. correlation_id = self.corr_id,
  7. ),
  1. # 消息需要为字符串
  2. body=str(n))
  3. while self.response is None:
  4. self.connection.process_data_events()
  5. return int(self.response)
  6. # 生成类的实例
  7. fibonacci_rpc = FibonacciRpcClient()
  8.  
  9. print(" [x] Requesting fib(30)")
  1. # 调用实例的call方法
  1. response = fibonacci_rpc.call(30)
  2. print(" [.] Got %r" % response)

 

 

Memcached

memcached是一个高性能的分布式内存对象缓存系统,用于动态web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态,数据库驱动网站的速度。

memcached基于一个存储键值对的hashmap。其守护进程(demeon)是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。

安装省略

启动memcached

  1. memcached -d -m 10 -u root -l 10.211.55.4 -p 12000 -c 256 -P /tmp/memcached.pid
  2.  
  3. 参数说明:
  4. -d 是启动一个守护进程
  5. -m 是分配给Memcache使用的内存数量,单位是MB
  6. -u 是运行Memcache的用户
  7. -l 是监听的服务器IP地址
  8. -p 是设置Memcache监听的端口,最好是1024以上的端口
  9. -c 选项是最大运行的并发连接数,默认是1024,按照你服务器的负载量来设定
  10. -P 是设置保存Memcachepid文件

 

memcached命令:

  1. 存储命令: set/add/replace/append/prepend/cas
  2. 获取命令: get/gets
  3. 其他命令: delete/stats..

 

第一次操作

  1. import memcache
  2.  
  3. mc = memcache.Client(['10.211.55.4:12000'], debug=True) #debug=True 表示运行出现错误时,显示错误信息,上线后移除该参数
  4. mc.set("foo", "bar")
  5. ret = mc.get('foo')
  6. print(ret)

天生支持集群

python-memcached模块原生支持集群操作,其原理是在内存维护一个i额主机列表,且集群中主机的权重值和主机在列表中重复出现的次数成正比。

如果用户在内存中创建一个键值对(如:k1 = ‘v1’),那么要执行以下步鄹:

  • 根据算法将k1转换成一个数字
  • 将数字和主机长度求余数,得到一个值N(0 <= N < 列表长度)
  • 在主机列表中根据第2步得到的值为索引获取主机,例如host_list[N]
  • 连接 将第3步中获取的主机,将 k1 = ‘v1’放置在该服务器的内存中

代码实现如下:

  1. mc = memcache.Client([('1.1.1.1:12000', 1), ('1.1.1.2:12000', 2), ('1.1.1.3:12000', 1)], debug=True)
  2.  
  3. mc.set('k1', 'v1')

add

添加一条键值对,如果已经存在的key,重复执行add操作异常

  1. import memcache
  2.  
  3. mc = memcache.Client(['10.211.55.4:12000'], debug=True)
  4. mc.add('k1', 'v1')
  5. # mc.add('k1', 'v2') # 报错,对已经存在的key重复添加,失败!!!

replace

replace修改某个key的值,如果key不存在,则异常

  1. import memcache
  2.  
  3. mc = memcache.Client(['10.211.55.4:12000'], debug=True)
  4. # 如果memcache中存在kkkk,则替换成功,否则异常
  5. mc.replace('kkkk','999')

set 和 set_multi

set                  设置一个键值对,如果key不存在,则创建,如果key存在,则修改

set_multi        设置多个键值对,如果key不存在,则创建,如果key存在,则修改

  1. import memcache
  2.  
  3. mc = memcache.Client(['10.211.55.4:12000'], debug=True)
  4.  
  5. mc.set('key0', 'wupeiqi')
  6.  
  7. mc.set_multi({'key1': 'val1', 'key2': 'val2'})

delete 和 delete_multi

delete               删除指定的一个键值对

delete_multi      删除指定的多个键值对

  1. import memcache
  2.  
  3. mc = memcache.Client(['10.211.55.4:12000'], debug=True)
  4.  
  5. mc.delete('key0')
  6. mc.delete_multi(['key1', 'key2'])

get 和 get_multi

get              获取一个键值对

get_multi     获取多个键值对

  1. import memcache
  2.  
  3. mc = memcache.Client(['10.211.55.4:12000'], debug=True)
  4.  
  5. val = mc.get('key0')
  6. item_dict = mc.get_multi(["key1", "key2", "key3"])

append 和 prepend

append                  修改指定key的值,在该值后面追加内容

prepend                 修改指定key的值,在该值前面插入内容

  1. import memcache
  2.  
  3. mc = memcache.Client(['10.211.55.4:12000'], debug=True)
  4. # k1 = "v1"
  5.  
  6. mc.append('k1', 'after')
  7. # k1 = "v1after"
  8.  
  9. mc.prepend('k1', 'before')
  10. # k1 = "beforev1after"

decr 和 incr

incr           自增,将某一个值增加N(默认为1)

decr          自减

  1. import memcache
  2.  
  3. mc = memcache.Client(['10.211.55.4:12000'], debug=True)
  4. mc.set('k1', '777')
  5.  
  6. mc.incr('k1')
  7. # k1 = 778
  8.  
  9. mc.incr('k1', 10)
  10. # k1 = 788
  11.  
  12. mc.decr('k1')
  13. # k1 = 787
  14.  
  15. mc.decr('k1', 10)
  16. # k1 = 777

gets 和 cas

如果商城剩余个数,假设count = 900

A用户刷新页面得到的的count = 900

B用户刷新页面得到的的count = 900

如果A,B都购买商品则缓存中的结构都是count = 899,但是实际不是这样

为了避免这样的情况发生,需要用到gets 和cas

  1. import memcache
  2. mc = memcache.Client(['10.211.55.4:12000'], debug=True, cache_cas=True)
  3.  
  4. v = mc.gets('product_count')
  5. # ...
  6. # 如果有人在gets之后和cas之前修改了product_count,那么,下面的设置将会执行失败,剖出异常,从而避免非正常数据的产生
  7. mc.cas('product_count', "899")

ps:本质上每次执行gets时,会从memcached中获取一个自增的数字,通过cas去修改gets的值时,会携带之前获取的自增值和memcache中的自增值进行比较,如果相等,则额可以提交,如果不相等,那么表示gets和cas执行之间,又有其他人执行了gets(获取了缓冲的指定值),可能出现非正常数据,则不允许修改。

 

Redis

redis是一个key-value存储系统,和memcache类似,它支持的Value类型相对更多,包括字符串,列表,set(集合),zset(有序集合),hash。这些数据类型都支持push,pop,add,remove及取交集并集差集及更丰富的操作。在此基础上,redis支持各种不同方式的排序。与memcache一样,为了保证效率,数据都是缓存在内存中,区别的是redis会周期性的把更新的数据写入磁盘或者把修改写入追加的记录文件,并且在此基础上实现了主从同步。每个redis有16个库,用户自己无法创建,默认使用第一个库(db = 0)。

操作模式

redis-py提供了两类redis和strictedis用于实现redis的命令,stricttedis用于实习大部分官方命令,并使用官方的语法和命令,reids是stricttedis的子类,用于向后兼容旧版本的redis-py

  1. import redis
  2.  
  3. r = redis.Redis(host='10.211.55.4', port=6379)
  4. r.set('foo', 'Bar')
  5. print(r.get('foo'))

连接池

redis-py使用connection pool 来管理对一个redis server的所有连接,避免每次建立,释放连接的开销。默认,每个redis实例都会维护一个自己的连接池,然后为参数reids,这样就可以实现多个redis实例共享一个连接池。

  1. import redis
  2.  
  3. pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
  4.  
  5. r = redis.Redis(connection_pool=pool)
  6. r.set('foo', 'Bar')
  7. print(r.get('foo'))

 

操作:

set(name,value,ex=None,px=None,nx=False,xx=False)

  1. Redis中设置值,默认,不存在则创建,存在则修改
  2. 参数:
  3. ex,过期时间(秒)
  4. px,过期时间(毫秒)
  5. nx,如果设置为True,则只有name不存在时,当前set操作才执行
  6. xx,如果设置为True,则只有name存在时,岗前set操作才执行

setnx(name,value)

  1. 设置值,只有name不存在时,执行设置操作(添加)

setex(name,value,time)

  1. # 设置值
  2. # 参数:
  3. # time,过期时间(数字秒 或 timedelta对象)

psetex(name,time_ms,value)

  1. # 设置值
  2. # 参数:
  3. # time_ms,过期时间(数字毫秒 或 timedelta对象)

mset(*args,**kwargs)

  1. 批量设置值
  2. 如:
  3. mset(k1='v1', k2='v2')

  4. mget({'k1': 'v1', 'k2': 'v2'})

get(name)

  1. #获取值

mget(keys,*args)

  1. 批量获取
  2. 如:
  3. mget('ylr', 'abc')

  4. r.mget(['ylr', 'abc'])

getset(name,value)

  1. # 设置新值并获取原来的值

getrange(key,start,end)

  1. # 获取子序列(根据字节获取,非字符)
  2. # 参数:
  3. # name,Redis 的 name
  4. # start,起始位置(字节)
  5. # end,结束位置(字节)
  6. # 如: "我是谁" ,0-3表示 "我"

setrange(name,offset,value)

  1. # 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
  2. # 参数:
  3. # offset,字符串的索引,字节(一个汉字三个字节)
  4. # value,要设置的值

setbit(name,offset,value)

  1. # 对name对应值的二进制表示的位进行操作
  2. # 因为值在redis里面是以字符形式存在的,所以这里在操作数字的时候,这里数字需要先找到对应的ASCII的值,把这个ASCII十进制数转换为二进制数,再进行操作
  3.  
  4. # 参数:
  5. # name,redis的name
  6. # offset,位的索引(将值变换成二进制后再进行索引)
  7. # value,值只能是 1 或 0
  8.  
  9. # 注:如果在Redis中有一个对应: n1 = "foo",
  10. 那么字符串foo的二进制表示为:01100110 01101111 01101111
  11. 所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1
  12. 那么最终二进制则变成 01100111 01101111 01101111,即:"goo"
  13.  
  14. # 扩展,转换二进制表示:
  15. source = "foo"
  16.  
  17. for i in source:
  18. num = ord(i)
  19. print(bin(num).replace('b',''))

getbit(name,offset)

  1. # 获取name对应的值的二进制表示中的某位的值 (0或1)

bitcount(key,start=None,end=None)

  1. # 获取name对应的值的二进制表示中 1 的个数
  2. # 参数:
  3. # key,Redis的name
  4. # start,位起始位置
  5. # end,位结束位置

bitop(operation,dest,*keys)

  1. # 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值
  2.  
  3. # 参数:
  4. # operation,AND(并) 、 OR(或) 、 NOT(非) 、 XOR(异或)
  5. # dest, 新的Redis的name
  6. # *keys,要查找的Redis的name
  7.  
  8. # 如:
  9. bitop("AND", 'new_name', 'n1', 'n2', 'n3')
  10. # 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算(求并集),然后将结果保存 new_name 对应的值中

strlen(name)

  1. # 返回name对应值的字节长度(一个汉字3个字节)

incr(self,name,amount=1)

  1. # 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
  2.  
  3. # 参数:
  4. # name,Redis的name
  5. # amount,自增数(必须是整数)
  6.  
  7. # 注:同incrby

incrbyfloat(self,name,amout=1.0)

  1. # 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
  2.  
  3. # 参数:
  4. # name,Redis的name
  5. # amount,自增数(浮点型)

decr(self,name,amout =1)

  1. # 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
  2.  
  3. # 参数:
  4. # name,Redis的name
  5. # amount,自减数(整数)

append(key,value)

  1. # 在redis name对应的值后面追加内容
  2.  
  3. # 参数:
  4. key, redisname
  5. value, 要追加的字符串

 

hash操作

hset(name,key,value)

  1. # name对应的hash中设置一个键值对(不存在,则创建;否则,修改)
  2.  
  3. # 参数:
  4. # name,redis的name
  5. # key,name对应的hash中的key
  6. # value,name对应的hash中的value
  7.  
  8. # 注:
  9. # hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)

hmset(name,mapping)

  1. # 在name对应的hash中批量设置键值对
  2.  
  3. # 参数:
  4. # name,redis的name
  5. # mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
  6.  
  7. # 如:
  8. # r.hmset('xx', {'k1':'v1', 'k2': 'v2'})

hget(name,key)

  1. # 在name对应的hash中获取根据key获取value

hmget(name,keys,*agrs)

  1. # 在name对应的hash中获取多个key的值
  2.  
  3. # 参数:
  4. # name,reids对应的name
  5. # keys,要获取key集合,如:['k1', 'k2', 'k3']
  6. # *args,要获取的key,如:k1,k2,k3
  7.  
  8. # 如:
  9. # r.mget('xx', ['k1', 'k2'])
  10. # 或
  11. # print(r.hmget('xx', 'k1', 'k2'))

hgetall(name)

  1. # 获取name对应hash的所有键值

hlen(name)

  1. # 获取name对应的hash中键值对的个数

hkeys(name)

  1. # 获取name对应的hash中所有的key的值

hvals(name)

  1. # 获取name对应的hash中所有的value的值

hexists(name,key)

  1. # 检查name对应的hash是否存在当前传入的key

hdel(name,*keys)

  1. # 将name对应的hash中指定key的键值对删除

hincrby(name,key,amount=1)

  1. # 自增name对应的hash中的指定key的值,不存在则创建key=amount
  2. # 参数:
  3. # name,redis中的name
  4. # key, hash对应的key
  5. # amount,自增数(整数)

hincrbyfloat(name,key,amount=1.0)

  1. # 自增name对应的hash中的指定key的值,不存在则创建key=amount
  2.  
  3. # 参数:
  4. # name,redis中的name
  5. # key, hash对应的key
  6. # amount,自增数(浮点数)
  7.  
  8. # 自增name对应的hash中的指定key的值,不存在则创建key=amount

hscan(name,cursor=0,match=None,count=None)

  1. # 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
  2.  
  3. # 参数:
  4. # name,redis的name
  5. # cursor,游标(基于游标分批取获取数据)
  6. # match,匹配指定key,默认None 表示所有的key
  7. # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
  8.  
  9. # 如:
  10. # 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
  11. # 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
  12. # ...
  13. # 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕

hscan_iter(name,match=None,count=None)

  1. # 利用yield封装hscan创建生成器,实现分批去redis中获取数据
  2.  
  3. # 参数:
  4. # match,匹配指定key,默认None 表示所有的key
  5. # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
  6.  
  7. # 如:
  8. # for item in r.hscan_iter('xx'):
  9. # print(item)

 

list操作

redis中的list在内存中按照一个name对应一个list来存储。

lpush(name,values)

  1. # 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
  2.  
  3. # 如:
  4. # r.lpush('oo', 11,22,33)
  5. # 保存顺序为: 33,22,11
  6.  
  7. # 扩展:
  8. # rpush(name, values) 表示从右向左操作

lpushx(name,value)

  1. # 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
  2.  
  3. # 更多:
  4. # rpushx(name, value) 表示从右向左操作

llen(name)

  1. # name对应的list元素的个数

linsert(name,where,refvale,value)

  1. # 在name对应的列表的某一个值前或后插入一个新值
  2.  
  3. # 参数:
  4. # name,redis的name
  5. # where,BEFORE或AFTER
  6. # refvalue,标杆值,即:在它前后插入数据
  7. # value,要插入的数据

r.lset(name,index,value)

  1. # 对name对应的list中的某一个索引位置重新赋值
  2.  
  3. # 参数:
  4. # name,redis的name
  5. # index,list的索引位置
  6. # value,要设置的值

r.lrem(name,vale,num)

  1. # 在name对应的list中删除指定的值
  2.  
  3. # 参数:
  4. # name,redis的name
  5. # value,要删除的值
  6. # num, num=0,删除列表中所有的指定值;
  7. # num=2,从前到后,删除2个;
  8. # num=-2,从后向前,删除2个

lpop(name)

  1. # 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
  2.  
  3. # 更多:
  4. # rpop(name) 表示从右向左操作

lindex(name,index)

  1. # 在name对应的列表中根据索引获取列表元素

lrange(name,start,end)

  1. # 在name对应的列表分片获取数据
  2. # 参数:
  3. # name,redis的name
  4. # start,索引的起始位置
  5. # end,索引结束位置

ltrim(name,start,end)

  1. # 在name对应的列表中移除没有在start-end索引之间的值
  2. # 参数:
  3. # name,redis的name
  4. # start,索引的起始位置
  5. # end,索引结束位置

rpoplpush(src,dst)

  1. # 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
  2. # 参数:
  3. # src,要取数据的列表的name
  4. # dst,要添加数据的列表的name

blpop(keys,timeout)

  1. # 将多个列表排列,按照从左到右去pop对应列表的元素
  2.  
  3. # 参数:
  4. # keys,redis的name的集合
  5. # timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞
  6.  
  7. # 更多:
  8. # r.brpop(keys, timeout),从右向左获取数据

brpoplpush(src,dst,timeout=0)

  1. # 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
  2.  
  3. # 参数:
  4. # src,取出并要移除元素的列表对应的name
  5. # dst,要插入元素的列表对应的name
  6. # timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞

自定义增量迭代

  1. # 由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要:
  2. # 1、获取name对应的所有列表
  3. # 2、循环列表
  4. # 但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能:
  5.  
  6. def list_iter(name):
  7. """
  8. 自定义redis列表增量迭代
  9. :param name: redis中的name,即:迭代name对应的列表
  10. :return: yield 返回 列表元素
  11. """
  12. list_count = r.llen(name)
  13. for index in xrange(list_count):
  14. yield r.lindex(name, index)
  15.  
  16. # 使用
  17. for item in list_iter('pp'):
  18. print(item)

 

set操作

set集合就是不允许重复的列表

sadd(name,values)

  1. # name对应的集合中添加元素

scard(name)

  1. # 获取name对应的集合中元素个数

sdiff(keys,*args)

  1. # 在第一个name对应的集合中且不在其他name对应的集合的元素集合

sdiffstore(dest,keys,*args)

  1. # 获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中

sinter(keys,*args)

  1. # 获取多一个name对应集合的并集

sinterstore(dest,keys,*args)

  1. # 获取多一个name对应集合的并集,再讲其加入到dest对应的集合中

sismember(name,value)

  1. # 检查value是否是name对应的集合的成员

smembers(name)

  1. # 获取name对应的集合的所有成员

smove(src,dst,value)

  1. # 将某个成员从一个集合中移动到另外一个集合

spop(name)

  1. # 从集合的右侧(尾部)移除一个成员,并将其返回

sranmember(name,numbers)

  1. # 从name对应的集合中随机获取 numbers 个元素

srem(name,values)

  1. # 在name对应的集合中删除某些值

sunion(keys,*args)

  1. # 获取多一个name对应的集合的并集

sunionstore(dest,keys,*args)

  1. # 获取多一个name对应的集合的并集,并将结果保存到dest对应的集合中

sscan(name,cursor=0,match=None,count=None)

sscan_iter(name,match=None,count=None)

  1. # 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大

 

有序集合

在集合的基础上,为每元素排序,元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数用来做排序

zadd(name,*args,**kwargs)

  1. # 在name对应的有序集合中添加元素
  2. # 如:
  3. # zadd('zz', 'n1', 1, 'n2', 2)
  4. # 或
  5. # zadd('zz', n1=11, n2=22)

zcard(name)

  1. # 获取name对应的有序集合元素的数量

zcount(name,min,max)

  1. # 获取name对应的有序集合中分数 在 [min,max] 之间的个数

zincrby(name,value,amount)

  1. # 自增name对应的有序集合的 name 对应的分数

r.zrange(name,start,end,desc=False,withscores=False,score_cast_func=float)

  1. # 按照索引范围获取name对应的有序集合的元素
  2.  
  3. # 参数:
  4. # name,redis的name
  5. # start,有序集合索引起始位置(非分数)
  6. # end,有序集合索引结束位置(非分数)
  7. # desc,排序规则,默认按照分数从小到大排序
  8. # withscores,是否获取元素的分数,默认只获取元素的值
  9. # score_cast_func,对分数进行数据转换的函数
  10.  
  11. # 更多:
  12. # 从大到小排序
  13. # zrevrange(name, start, end, withscores=False, score_cast_func=float)
  14.  
  15. # 按照分数范围获取name对应的有序集合的元素
  16. # zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)
  17. # 从大到小排序
  18. # zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)

zrank(name,value)

  1. # 获取某个值在 name对应的有序集合中的排行(从 0 开始)
  2.  
  3. # 更多:
  4. # zrevrank(name, value),从大到小排序

zrangebylex(name,min,max,start=None,num=None)

  1. # 当有序集合的所有成员都具有相同的分值时,有序集合的元素会根据成员的 值 (lexicographical ordering)来进行排序,而这个命令则可以返回给定的有序集合键 key 中, 元素的值介于 min 和 max 之间的成员
  2. # 对集合中的每个成员进行逐个字节的对比(byte-by-byte compare), 并按照从低到高的顺序, 返回排序后的集合成员。 如果两个字符串有一部分内容是相同的话, 那么命令会认为较长的字符串比较短的字符串要大
  3.  
  4. # 参数:
  5. # name,redis的name
  6. # min,左区间(值)。 + 表示正无限; - 表示负无限; ( 表示开区间; [ 则表示闭区间
  7. # min,右区间(值)
  8. # start,对结果进行分片处理,索引位置
  9. # num,对结果进行分片处理,索引后面的num个元素
  10.  
  11. # 如:
  12. # ZADD myzset 0 aa 0 ba 0 ca 0 da 0 ea 0 fa 0 ga
  13. # r.zrangebylex('myzset', "-", "[ca") 结果为:['aa', 'ba', 'ca']
  14.  
  15. # 更多:
  16. # 从大到小排序
  17. # zrevrangebylex(name, max, min, start=None, num=None)

zrem(name,values)

  1. # 删除name对应的有序集合中值是values的成员
  2.  
  3. # 如:zrem('zz', ['s1', 's2'])

zremrangebyrank(name,min,max)

  1. # 根据排行范围删除

zremrangebyscore(name,min,max)

  1. # 根据分数范围删除

zermrangebylex(name,min,max)

  1. # 根据值返回删除

zscore(name,value)

  1. # 获取name对应有序集合中 value 对应的分数

zinterstore(dest,keys,aggregate=None)

  1. # 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作
  2. # aggregate的值为: SUM MIN MAX

zunionstore(dest,keys,aggregate=None)

  1. # 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作
  2. # aggregate的值为: SUM MIN MAX

zscan(name,cursor=0,match=None,count=None,score_cast_func=float)

zscan_iter(name,match=None,conut=None,socre_cast_func=float)

  1. # 同字符串相似,相较于字符串新增score_cast_func,用来对分数进行操作

 

其他常用操作

delete(*names)

  1. # 根据删除redis中的任意数据类型

exists(name)

  1. # 检测redis的name是否存在

keys(pattern=’*’)

  1. # 根据模型获取redis的name
  2.  
  3. # 更多:
  4. # KEYS * 匹配数据库中所有 key 。
  5. # KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
  6. # KEYS h*llo 匹配 hllo 和 heeeeello 等。
  7. # KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo

expire(name,time)

  1. # 为某个redis的某个name设置超时时间

rename(src,dst)

  1. # 对redis的name重命名为

move(name,db)

  1. # 将redis的某个值移动到指定的db下

randomkey()

  1. # 随机获取一个redis的name(不删除)

type(name)

  1. # 获取name对应值的类型

scan(cursor=0,match=None,count=None)

scan_iter(match=None,count=None)

  1. # 同字符串操作,用于增量迭代获取key

 

管道

redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令。

  1. import redis
  2.  
  3. pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
  4.  
  5. r = redis.Redis(connection_pool=pool)
  6.  
  7. # pipe = r.pipeline(transaction=False)
  8. pipe = r.pipeline(transaction=True)
  9.  
  10. r.set('name', 'alex')
  11. r.set('role', 'sb')
  12.  
  13. pipe.execute()

 

发布订阅

例子:

monitor  代码:

  1. import redis
  2.  
  3. class RedisHelper:
  4.  
  5. def __init__(self):
  6. self.__conn = redis.Redis(host='10.211.55.4')
  7. self.chan_sub = 'fm104.5'
  8. self.chan_pub = 'fm104.5'
  9.  
  10. def public(self, msg):
  11. self.__conn.publish(self.chan_pub, msg)
  12. return True
  13.  
  14. def subscribe(self):
  15. pub = self.__conn.pubsub()
  16. pub.subscribe(self.chan_sub)
  17. pub.parse_response()
  18. return pub

订阅者:

  1. from monitor.RedisHelper import RedisHelper
  2.  
  3. obj = RedisHelper()
  4. redis_sub = obj.subscribe()
  5.  
  6. while True:
  7. msg= redis_sub.parse_response()
  8. print(msg)

发表者:

  1. from monitor.RedisHelper import RedisHelper
  2.  
  3. obj = RedisHelper()
  4. obj.public('hello')

RabbitMQ队列,Redis\Memcached缓存的更多相关文章

  1. RabbitMQ队列/Redis缓存

    一.RabbitMQ队列 RabbitMQ安装(Centos7安装):1.安装依赖:yum install socat (不安装会报错)2.下载rpm包:wget http://www.rabbitm ...

  2. python RabbitMQ队列/redis

    RabbitMQ队列 rabbitMQ是消息队列:想想之前的我们学过队列queue:threading queue(线程queue,多个线程之间进行数据交互).进程queue(父进程与子进程进行交互或 ...

  3. springboot2.0+redis实现消息队列+redis做缓存+mysql

    本博客仅供参考,本人实现没有问题. 1.环境 先安装redis.mysql 2.springboot2.0的项目搭建(请自行完成),本人是maven项目,因此只需配置,获取相应的jar包,配置贴出. ...

  4. Day10 - Python协程、异步IO、redis缓存、rabbitMQ队列

    Python之路,Day9 - 异步IO\数据库\队列\缓存   本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitM ...

  5. Day10-Python3基础-协程、异步IO、redis缓存、rabbitMQ队列

    内容目录: Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 Redis\Memcached缓存 Paramiko S ...

  6. Python之协程、异步IO、redis缓存、rabbitMQ队列

    本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 Redis\Memcached缓存 Paramiko SS ...

  7. 异步IO\数据库\队列\缓存\RabbitMQ队列

    本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 Redis\Memcached缓存 Paramiko SS ...

  8. 缓存、队列(Memcached、redis、RabbitMQ)

    本章内容: Memcached 简介.安装.使用 Python 操作 Memcached 天生支持集群 redis 简介.安装.使用.实例 Python 操作 Redis String.Hash.Li ...

  9. Python自动化 【第十一篇】:Python进阶-RabbitMQ队列/Memcached/Redis

     本节内容: RabbitMQ队列 Memcached Redis 1.  RabbitMQ 安装 http://www.rabbitmq.com/install-standalone-mac.htm ...

随机推荐

  1. xcode6

    官方的xcode6下载太慢,这里送上百度网盘地址: http://pan.baidu.com/s/1hqze1hi

  2. Google的Guava之IO升华

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/luo201227/article/details/36413279 程序员在开发过程中,使用文件的几 ...

  3. Django模型系统——ORM中跨表、聚合、分组、F、Q

    核心知识点: 1.明白表之间的关系 2.根据关联字段确定正反向,选择一种方式 在Django的ORM种,查询既可以通过查询的方向分为正向查询和反向查询,也可以通过不同的对象分为对象查询和Queryse ...

  4. J2EE SSH框架整合教程

    本文仅作为学习和研究的参考,与实际项目使用技术有所不同,由于作者水平有限,错误疏漏在所难免,请各位看官批评指教. 项目的源代码放在:https://github.com/Frank-Pei/SSHIn ...

  5. origin与referer的区别

    referer显示来源页面的完整地址,而origin显示来源页面的origin: protocal+host,不包含路径等信息,也就不会包含含有用户信息的敏感内容 referer存在于所有请求,而or ...

  6. 20145239 GDB调试汇编堆栈过程分析

    20145239 GDB调试汇编堆栈过程分析 测试源代码 #include<stdio.h> ; ; ; static int g(int x) { return x + addend1; ...

  7. 广义表(C++实现)

    广义表是非线性结构,其定义是递归的. 以下给出几种简单的广义表模型: 由上图我们可以看到,广义表的节点类型无非head.value.sub三种,这里设置枚举类型,利用枚举变量来记录每个节点的类型: e ...

  8. SPFA 算法(剪辑)(学习!)

    SPFA算法 单源最短路径的算法最常用的是Dijkstra,些算法从时间复杂度来说为O(n^2),但是面对含有负权植的图来说就无能为力了,此时 Dellman-ford算法就有用了,这咱算法是采用的是 ...

  9. Linux初识(命令, 文件, 系统管理)

    Linux初识(命令, 文件) 文件系统 在Linux系统下,没有驱动器磁盘,只有一个根目录 / ,所有的文件都在根目录下面. 相关文件夹介绍 bin : 程序相关 boot : 开机启动相关 cdr ...

  10. EntityFramework 学习 一 Stored Procedure

    CREATE PROCEDURE [dbo].[GetCoursesByStudentId] -- Add the parameters for the stored procedure here @ ...