day11--RabbitMQRedisMysql

Posted (野生程序员)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了day11--RabbitMQRedisMysql相关的知识,希望对你有一定的参考价值。

    RabbitMQ:就是消息队列与Python里面的queue功能类似。线程和进程queue只能Python自己使用;不同机器和程序传递消息就要使用RabbitMQ了,中间传递。

    RabbitMQ的功能与Queue的功能一样,不同的是,线程queue.Queue()用来不同线程之间进行传递消息;进程Queue()用来子进程和父进程之间的数据交互;或者同属于同一父进程下多个子进程进行交互。

    RabbitMQ可以实现多个程序之间的数据交互,不需要让程序去各自维护,统一通过中间商,RabbitMQ实现即可。

    RabbitMQ地址:(http://erlang.org/download/?S=A/snapshots/otp_src_R14B01.readme/otp_doc_man_R16A_RELEASE_CANDIDATE.tar.gz/otp_win32_R10B-1a.exe/otp_doc_html_R16B.tar.gz/otp_doc_man_R10B-5.tar.gz)

 

    RabbitMQ是使用erlang开发的

    RabbitMQ队列

    安装 http://www.rabbitmq.com/install-standalone-mac.html

    安装python rabbitMQ module

    pip install pika

    easy_install pika

    https://pypi.python.org/pypi/pika

    实现最简单的队列通信   

    由于Linux电脑尝试了很多次,配置没有弄好,参考网址:https://www.cnblogs.com/alex3714/articles/5248247.html

    send端

 

import pika
 
connection = pika.BlockingConnection(pika.ConnectionParameters(
               \'localhost\'))
channel = connection.channel()                       #声明一个管道
 
#声明queue
channel.queue_declare(queue=\'hello\')                 #声明一个"hello"的queue
 
#n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
channel.basic_publish(exchange=\'\',
                      routing_key=\'hello\',
                      body=\'Hello World!\')           #routing_key是queue的名称
print(" [x] Sent \'Hello World!\'")
connection.close()

 

    receive端

import pika
 
connection = pika.BlockingConnection(pika.ConnectionParameters(
               \'localhost\'))
channel = connection.channel()
 
 
#You may ask why we declare the queue again ‒ we have already declared it in our previous code.
# We could avoid that if we were sure that the queue already exists. For example if send.py program
#was run before. But we\'re not yet sure which program to run first. In such cases it\'s a good
# practice to repeat declaring the queue in both programs.
channel.queue_declare(queue=\'hello\')
 
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
 
channel.basic_consume(callback,            #如果收到消息,就调用callback函数来处理消息
                      queue=\'hello\',       #从那个队列里面接收消息
                      no_ack=True)         #不确认,no_ack=True,服务器端不确认;
 
print(\' [*] Waiting for messages. To exit press CTRL+C\')
channel.start_consuming()

      Work Queues

   

    在这种模式下,RabbitMQ会默认把p发的消息依次分发给各个消费者(c),跟负载均衡差不多

    Redis操作(http://www.cnblogs.com/wupeiqi/articles/5132791.html)(http://www.cnblogs.com/alex3714/articles/6217453.html)(https://www.cnblogs.com/alex3714/articles/5248247.html)

    正常程序之间可以通过json和pickle传递数据,是通过文件。但是文件之间传输效率太低了。因此引入了缓存,缓存是放在内存中(硬盘效率太低),因此找一个中间媒介。现在常用的就是redis,下面来了解一下:

    redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在次基础上实现了master-slave(主从)同步。  

    Redis优点

    1.异常快速 : Redis是非常快的,每秒可以执行大约110000设置操作,81000个/每秒的读取操作;

    2.支持丰富的数据类型 : Redis支持最大多数开发人员已经知道如列表,集合,可排序集合,哈希等数据类型;这使得在应用中很容易解决的各种问题,因为我们知道哪些问题处理使用哪种数据类型更好解决;

    3.操作都是原子的 : 所有 Redis 的操作都是原子,从而确保当两个客户同时访问 Redis 服务器得到的是更新后的值(最新值);

    4.MultiUtility工具:Redis是一个多功能实用工具,可以在很多如:缓存,消息传递队列中使用(Redis原生支持发布/订阅),在应用程序中,如:Web应用程序会话,网站页面点击数等任何短暂的数据;

    安装Redis环境

    一、Redis安装和基本使用

1 wget http://download.redis.io/releases/redis-3.0.6.tar.gz
2 tar xzf redis-3.0.6.tar.gz
3 cd redis-3.0.6
4 make

    启动服务端

src/redis-server

    启动客户端

1 src/redis-cli
2 redis> set foo bar
3 OK
4 redis> get foo
5 "bar"

    二、Python操作Redis

    Python操作Redis

sudo pip install redis
or
sudo easy_install redis
or
源码安装
  
详见:https://github.com/WoLpH/redis-py

     缓存里面的数据是共享的,是存储在内存中,硬盘的效率太低,redis缓存也是通过socket基础原理来实现数据的共享。redis就是一个类似字典key-value的缓存系统。

    API使用

    redis-py 的API的使用可以分类为:

    1.连接方式

    2.连接池

    3.操作

        String 操作         #简单的keys-value操作

        Hash 操作

        List 操作

        Set 操作

        Sort Set 操作       #有序集合

    4.管道

    5.发布订阅

    1、操作模式

     redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。

import redis
 
r = redis.Redis(host=\'10.211.55.4\', port=6379)              #连接IP地址和端口号
r.set(\'foo\', \'Bar\')
print r.get(\'foo\')

  使用redis设置属性:

  127.0.0.1:6379> set name alex
  OK
  127.0.0.1:6379> set age 22
  OK

    set设置 name名字 alex值

    keys *查看属性

    get来获取属性值(get name、get age)实现了两个程序之间的数据共享,QQ、微信都能来取数据。    如果使用get获取属性,属性不存在,则为空,如下:

    127.0.0.1:6379> get name
  "alex"
  127.0.0.1:6379> get foo
  "bar"
  127.0.0.1:6379> get alex
  (nil)
  127.0.0.1:6379> get age
  "22"

    set使用方式:

    set name alex ex 2中,ex 2代表设置存活时间,2代表两秒,exists存在。set设置属性的存活时间。

    2、连接池

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

 

import redis

pool = redis.ConnectionPool(host="127.0.0.1",port=6379) #设置一个连接池,所有程序都从这个池子里面取数据
r = redis.Redis(connection_pool=pool) #建立一个连接实例
r.set("school","oldboy")
print(r.get("school"))
运行结果如下:
b\'oldboy\'

 

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

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

    nx属性是,如果属性不存在,才设置,否则不会设置,如下:

127.0.0.1:6379[1]> set name alex
OK
127.0.0.1:6379[1]> set name Alex nx
(nil)
127.0.0.1:6379[1]> get name
"alex"

    上面,我们设置了name属性,接着我们来覆盖这个属性,正常如果没有nx属性,"alex"将被“Alex"值覆盖,但是这里设置了nx,由于name存在,不能覆盖。nx的作用就是如果属性存在,则设置不成功,否则设置成功。

    xx只有当属性存在的时候,操作才成功,即只覆盖原来属性,原来属性不存在不会创建新的属性,如下:

127.0.0.1:6379[1]> get name
"alex"
127.0.0.1:6379[1]> set name Alex xx
OK
127.0.0.1:6379[1]> get name
"Alex"
127.0.0.1:6379[1]> set age 22 xx
(nil)

    从上面程序可以看出,xx是当属性不存在的时候不会设置成功,只有当属性存在了,覆盖原来属性,是用来覆盖原来属性的,属性存在覆盖,不存在不会创建。

    setnx(name, value)

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

    setex(name, time, value)

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

    psetex(key,milliseconds,value)

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

    mset(*args, **kwargs)

批量设置值
如:
    mset(k1=\'v1\', k2=\'v2\')
    或
    mget({\'k1\': \'v1\', \'k2\': \'v2\'})

    批量设置参数,如下:

    127.0.0.1:6379[1]> mset k1 v1 k2 v2
  OK
  127.0.0.1:6379[1]> get k1
  "v1"
  127.0.0.1:6379[1]> get k2
  "v2"

    get(name)

    获取值

    mget(keys, *args)    

批量获取
如:
    mget(\'ylr\', \'wupeiqi\')
    或
    r.mget([\'ylr\', \'wupeiqi\'])

    mget(keys,*args)是批量获取值,如下:

    127.0.0.1:6379[1]> mget k1 k2
  1) "v1"
  2) "v2"

    getset(name, value)

    设置新值并获取原来的值

    getset(name,value)是设置并获取原来的新值,如果原来的值不存在,则(nil),如果原来的值存在,获取原来的值,并把新值赋值给原来的属性。如下:

    127.0.0.1:6379[1]> getset k1 alex                    #给k1设置新值,并获取k1原来的值
  "v1"                                                        #获取到了k1原来的值
  127.0.0.1:6379[1]> getset k8 tom
  (nil)                                                       #属性k8不存在,则获取不到原来的值,但是可以设置k8=tom
  127.0.0.1:6379[1]> get k1
  "alex"                                                      #属性k1的值重新设置了

    127.0.0.1:6379[1]> get k8
  "tom"                                                       #虽然k8不存在,但是可以设置

    getrange(key, start, end)

 

# 获取子序列(根据字节获取,非字符)
# 参数:
    # name,Redis 的 name
    # start,起始位置(字节)
    # end,结束位置(字节)
# 如: "武沛齐"0-3表示 ""

 

    127.0.0.1:6379> getrange school 0 2
  "old"

    getrange是获取键值的切片长度,字符长度。中文是以字节形式获取的,在redis里面,都是以字节形式存储的。

    127.0.0.1:6379> getrange myname 0 2
  "\\xe8\\x80\\xbf"

    setrange(name, offset, value)

 

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

 

    setrange(name,offset,value)从某个设定的位置开始覆盖,如下:

    127.0.0.1:6379> get school
  "oldboy"
  127.0.0.1:6379> setrange school 2 alex
  (integer) 6
  127.0.0.1:6379> get school
  "olalex"

    setrange(name,offset,value)向下偏移offset个字符,覆盖新的值value

    setbit(name, offset, value)

# 对name对应值的二进制表示的位进行操作
 
# 参数:
    # name,redis的name
    # offset,位的索引(将值变换成二进制后再进行索引)
    # value,值只能是 10
 
# 注:如果在Redis中有一个对应: n1 = "foo",
        那么字符串foo的二进制表示为:01100110 01101111 01101111
    所以,如果执行 setbit(\'n1\', 7, 1),则就会将第7位设置为1,
        那么最终二进制则变成 01100111 01101111 01101111,即:"goo"
 
# 扩展,转换二进制表示:
 
    # source = "武沛齐"
    source = "foo"
 
    for i in source:
        num = ord(i)
        print bin(num).replace(\'b\',\'\')
 
    特别的,如果source是汉字 "武沛齐"怎么办?
    答:对于utf-8,每一个汉字占 3 个字节,那么 "武沛齐" 则有 9个字节
       对于汉字,for循环时候会按照 字节 迭代,那么在迭代时,将每一个字节转换 十进制数,然后再将十进制数转换成二进制
        11100110 10101101 10100110 11100110 10110010 10011011 11101001 10111101 10010000
        -------------------------- ----------------------------- -----------------------------
                    武                         沛                           齐

    bitcount统计列表中1的个数,bitcount,假如要统计一个网站的人数,可以使用数据库来进行统计,可以通过设置不存在的变量来统计网站在线的人数,比如一个人登录,就把这个人的ID设置为1,根据网站注册人的ID编号,即是第几个用户,然后设置一个不存在的属性,如下:

127.0.0.1:6379> setbit n1 1 1
(integer) 0
127.0.0.1:6379> setbit n1 2 1
(integer) 0
127.0.0.1:6379> setbit n1 5 1
(integer) 0
127.0.0.1:6379> bitcount n1
(integer) 3
通过每次用户登录之后,把用户的二进制设置为1,统计1的个数,就能统计人数
上面通过设置,在n1中有三个1存在。

    setbit(name,offset,value)是按照二进制形式存在的,10101010001001111010011100011100111111100000

    getbit(name, offset)

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

    127.0.0.1:6379> getbit n1 1
  (integer) 1
  127.0.0.1:6379> getbit n1 10
  (integer) 0

    通过获取二进制位的值,1代表登录,0代表未登录。

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

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

    bitcount(key,start=None,end=None)统计属性值里面二进制1的个数,比如10100001里面有3个1,bitcount的值就为3.

    bitop(operation, dest, *keys)

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

    strlen(name)

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

    127.0.0.1:6379> get school
  "olalex\\x00\\x00\\x00\\x00tom"
  127.0.0.1:6379> strlen school
  (integer) 13

    获取字节的长度,strlen(name)是获取属性值的字节长度。

    incr(self, name, amount=1)

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

 

    127.0.0.1:6379> incr n4
  (integer) 1
  127.0.0.1:6379> incr n4
  (integer) 2
  127.0.0.1:6379> incr n4
  (integer) 3

    自增属性,当有新值存在是,自动增加1

    incrbyfloat(self, name, amount=1.0)

 

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

 

    可以设置定值增加,incr是以1每次递增,incrbyfloat可以以amout=n以任何形式的值自增,如下:

    127.0.0.1:6379> incrbyfloat n4 3
  "8"

    append(key, value)

 

# 在redis name对应的值后面追加内容
 
# 参数:
    key, redis的name
    value, 要追加的字符串

 

    127.0.0.1:6379> get age
  "22"
  127.0.0.1:6379> append age tom
  (integer) 5
  127.0.0.1:6379> get age
  "22tom"
    append(key,value)字符串的拼接,属性拼接值。

    Hash操作,redis中Hash在内存中的存储格式如下图:

    

    hash操作其实就是字典的操作,name相当于字典名,hash是字典里面的键值对,如dict={"k1":"v1","k2":"v2"}就是这样的键值对构成,而name属性就是这个字典的名称。

    hset(name, key, value)

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

    hset(name,key,value)其中,name就相当于字典的名,key键,value值,如下:

    127.0.0.1:6379> hset info name alex                    #定义一个字典info,name=alex
  (integer) 1

    127.0.0.1:6379> hset info age 38                       #添加键值对age=38
  (integer) 1

    hgetall name:获取name属性(字典中)所有键和值

    127.0.0.1:6379> hgetall info
  1) "name"
  2) "alex"
  3) "age"
  4) "38"
  5) "id"
  6) "3344"

    hget(name,key)

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

    127.0.0.1:6379> hget info name
  "alex"
  127.0.0.1:6379> hget info age
  "38"

    hkeys name:查看所有name属性(字典)中的键

    127.0.0.1:6379> hkeys info
  1) "name"
  2) "age"
  3) "id"
    hvals name:获取所有name属性(字典)中的值    127.0.0.1:6379> hvals info
  1) "alex"
  2) "38"
  3) "3344"

    hmset(name, mapping)

 

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

 

    127.0.0.1:6379> hmset info2 name wupeiqi age 33 id 3636                #批量设置属性键-值
  OK
  127.0.0.1:6379> hkeys info2
  1) "name"
  2) "age"
  3) "id"
    hmget(name, keys, *args)

# 在name对应的hash中获取多个key的值
 
# 参数:
    # name,reids对应的name
    # keys,要获取key集合,如:[\'k1\', \'k2\', \'k3\']
    # *args,要获取的key,如:k1,k2,k3
 
# 如:
    # r.mget(\'xx\', [\'k1\', \'k2\'])
    # 或
    # print r.hmget(\'xx\', \'k1\', \'k2\')

    hmget(name,keys,*args)批量获取值,如下:

    127.0.0.1:6379> hmget info2 name age id          #同时获取info2中的name,age,id键值
  1) "wupeiqi"
  2) "33"
  3) "3636"

    hlen(name)
     # 获取name对应的hash中键值对的个数

 

    127.0.0.1:6379> hlen info2                        #hlen info2获取info2中的键的个数
  (integer) 3

    hexists(name, key)

    # 检查name对应的hash是否存在当前传入的key,即判断name字典中是否包含Key

    127.0.0.1:6379> hexists info2 sex
  (integer) 0
  127.0.0.1:6379> hexists ifno2 name
  (integer) 0
  127.0.0.1:6379> hexists info2 age
  (integer) 1

    hexists(name,key)判断name字典中是否存在键key,存在返回1;不存在,返回0

    hdel(name,*keys)
    # 将name对应的hash中指定key的键值对删除
    127.0.0.1:6379> hvals info2
  1) "wupeiqi"
  2) "33"
  3) "3636"
  4) "female"
  127.0.0.1:6379> hdel info2 sex            #删除info2字典中的sex键值对
  (integer) 1
  127.0.0.1:6379> hvals info2
  1) "wupeiqi"
  2) "33"
  3) "3636"

    hincrby(name, key, amount=1)

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

 

    HINCRBY key field increment

    127.0.0.1:6379> hincrby info2 age 1
  (integer) 34

    hincrby info2 age 1意思是:info2字典中age键的值增加1,只有整数才能增加,info2中的name是不可以的,会报错,ERR hash value is not an integer,提示hash值不是整数。

    hincrbyfloat(name, key, amount=1.0)

 

# 自增name对应的hash中的指定key的值,不存在则创建key=amount
 
# 参数:
    # name,redis中的name
    # key, hash对应的key
    # amount,自增数(浮点数)
 
# 自增name对应的hash中的指定key的值,不存在则创建key=amount

 

    自增函数,与上面一样,只是可以使用浮点数

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

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

    hscan(name,cursor=0,match=None,count=None)是用来模糊匹配字典中的属性。

    127.0.0.1:6379> hscan info3 0 match k*          #获取info3中以k开头的键值对
  1) "0"
  2) 1) "k1"
    2) "v1"
    3) "k2"
    4) "v2"
    5) "k3"
    6) "v3"
    *代表所有,k*代表以*开头。

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

 

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

 

    List操作,redis中的List在在内存中按照一个name对应一个List来存储。如图:

    redis中的列表就是我们常用的列表,只是方式不一样,定义列表名和列表中的值,等价于name=list()

    lpush(name,values)

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

   lpush(name,values)就是定义一个名字为name的列表,往里面添加值,如下:

    127.0.0.1:6379> lpush names alex geng tom wupeiqi    #定义了一个名字为names的列表,并添加alex,geng,tom,wupeiqi四个值到列表中
  (integer) 4

    lrange name start end:查看列表中起始值,相当于切片,下面查看列表中所有的值:

    127.0.0.1:6379> lrange names 0 -1           #查看列表names中所有的值
  1) "wupeiqi"
  2) "tom"
  3) "geng"
  4) "alex"

    rpush(name,values)

    # 在name对应的list中添加元素,每个新的元素都添加到列表的最右边

    127.0.0.1:6379> rpush names 1 2 3
  (integer) 7

    往names列表的右边添加元素

    lpushx(name,value)

 

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

 

    127.0.0.1:6379> lpushx ages 22          #列表ages不存在,不能插入值
  (integer) 0

  127.0.0.1:6379> lpushx names cang       #列表names存在,可以插入值
  (integer)

    lpustx(name,value)是向列表中插入值,如果列表存在则插入,如果列表不存在,则不会创建新的列表,不能插入值。

    llen(name)
    # name对应的list元素的个数
    127.0.0.1:6379> llen names
  (integer) 8  

    llen(name)是查看列表的长度,即列表中有几个元素。

    linsert(name, where, refvalue, value)