Redis python搭建分布式的基本操作

用python操作redis浅浅地实现分布式

写在前面:系统的ubuntu14.04,语言:python
ubuntu下载的redis的方式很多,都是直接下载,不用安装的,很绿色,很环保。只要进入到对应的目录下唉启动文件就可以啦。
以我下载的版本为例:
- 第一步找到../redis-3.2.0/src/redis-server文件,在终端下找到该文件
- redis-server
- 运行面的命令,即可启动redis的服务,运行时可能会报错,可以参考我的另一文章。
- 进入redis界面:运行命令redis-cli即可进入到127.0.0.1:6379> 这样的界面啦。如果数据库里包含中文的结果,那就需要运行redis-cli --raw这个形式才可以显示中文。
- 运行redis的命令,即可操作redis了。可以到http://redis.io/commands这个官网查看对应的命令。
***************前言end************************

  • 在进入主题前有必要了解一下redis这个轻量级的数据库。了解一下它的基本语法和相关的配置,这是我学习基本后用python操作redis的基本代码。直接上代码↓↓↓
# coding=utf-8
import redis
class RedisQueue(object):
    def __init__(self, name, namespace=''):
        """
        连接数据库
        :param name:对应redis的数据库的键
        :param namespace:
        :return:
        """
        self.__db = redis.Redis(host='192.168.**.**', port=6379, db=0)  # 127.0.0.1,#这里是关键,是连接数据库的配置ip
        self.key = '%s%s' % (namespace, name)
        print self.key

    def qsize(self):
        """
        对应键的大小
        :return:
        """
        return self.__db.llen(self.key)

    def empty(self):
        """
        是否为空
        :return:
        """
        return self.qsize() == 0

    def put(self, item):
        """
        放进对应键的值,插入
        :param item:
        :return:
        """
        self.__db.rpush(self.key, item)

    def get(self, block=True, timeout=None):
        """
        """
        if block:
            item = self.__db.blpop(self.key, timeout=timeout)
            # Redis Blpop 命令移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
            # 如果列表为空,返回一个 nil 。 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。
        else:
            # Redis Lpop 命令用于移除并返回列表的第一个元素。
            item = self.__db.lpop(self.key)

        if item:
            item = item[1]

        return item

    def get_lrange(self, start, end):
        """
        取对应位置的值
        :param end:
        :return:
        """
        return self.__db.lrange(self.key, start, end)

    def del_key(self):
        """
        删除键
        :return:
        """
        self.__db.delete(self.key)

    def exists_key(self, keys):
        """
        判断是否存在
        """
        return self.__db.exists(keys) == 1

    def get_key(self,key_pattern):
        """
        获取模式下的键
        """
        return self.__db.keys(key_pattern)

以上代码是直接对redis操作的代码,思路很简单——主要是连接到对应的redis库就可以用对应的redis语法进行操作。
那么怎么实现分布式??我是这样做的,有两个方法:

  1. ①首先让分机对他进行分批,这样每台机子都可以进行对应的分批任务,大家都各自完成自己的任务就好。
  2. ②采用队列的方式:让分机直接对主机进行队列的操作,哪个分机都可以进行提取任务,对应的任务被提取后就删除,这样多台机子都可以进行排队的分布式操作。大家共同完成一个队列的任务。
    不管哪个方法,都需要先在主机上进行任务的导入,把任务导入redis
    下面将讲述怎么把数据导入redis

有了基本的操作后,我们将使用python进行redis的使用。

# coding=utf-8
from Queue import Queue
import re

def set_key(redis_key):
    """
    以事件为redis的key
    :param redis_key:事件
    :return:
    """
    global redis
    redis = RedisQueue(redis_key)


def read_txt(file_name):
    """
    打开文本,把对应的内容放进队列
    :param file_name: 文本所在的位置
    :return:
    """
    queue = Queue()
    txt = open(file_name, 'r')
    words = txt.readlines()
    for i in range(len(words)):
        words[i] = words[i].replace('\n', '')
        queue.put(words[i])//把每一行的人名放进队列
    # print 'There are %d username11' % queue.qsize()
    return queue

def main_put():
    """
    把队列的内容放进redis
    :return:
    """
        value=""#对应的文件路径
        queue = read_txt(value) #数据放进队列了 
        while not queue.empty():  # 如果队列不空
            username = queue.get()  # 取第一个然后移出
            # print username
            redis.put(username)  插入redis数据库,调用类RedisQueue的put方法
        print 'There are %d username' % redis.qsize()

# main_put()

- redis有导入数据后,那就需要进行下一步的分布式完成全部任务
上文已经把数据导入redis了,那么分布式的操作也提到了两个方法,那么就先用第一中方法:——redis的数据分批

# coding=utf-8

import redis

downresult_dir = {}

def get_redis_key():
    """
    连接数据库取出数据库的key, 然后生成对应的key的文件夹,以便保存结果
    :return:
    """
    db = redis.Redis(host='192.168.**.**', port=6379, db=0)  # 192.168.**.**,这个关键啦,需要连接对应的数据库ip
    if db.dbsize():
        kes = db.keys("*") #根据模式,取出数据库,这里书全部匹配都取出。
        for i in kes:
            new_dir = os.path.join(BASE_DIR,'documents','topic',i)
            is_exists = os.path.exists(new_dir)
            if not is_exists:
                os.makedirs(new_dir)
            downresult_dir.setdefault(i, new_dir)
        return True
    else:
        return False


def main_headunter(username):
    """
    我用了爬虫的方法,爬用户的昵称
    :param username: 用户的昵称
    :return:
    """
    a = Sina('/n/' + str(username))
    a.get_result()


def down_username(slave_num, your_id, redis_event):
    """
    下载redis的username,进行爬取资料
    :param slave_num: 分机的数量
    :param your_id: 分机的编号,1,2,3,4....
    :return:
    """
    average = redis_event.qsize() / int(slave_num)
    slave_num = {
    1: [0, average], 2: [average + 1, average * 2], 3: [average * 2 + 1, average * 3]}

    start_num = slave_num[your_id][0]
    end_num = slave_num[your_id][1]
    username = redis_event.get_lrange(start_num, end_num)

    try:
        pool = multiprocessing.Pool(processes=8)
        print 'username>>>>>>>>>>>>>>', len(username)
        for name_index in xrange(len(username)):
            pool.apply_async(main_headunter, (username[name_index],)) #多进程调用了爬虫方法,这里用进程池 apply_async
        pool.close()
        pool.join()
    except Exception, e:
        print e
        pass

def main_down_stats(SLAVENUM, YOURID):
    """
    对数据库的多个事件处理
    :return:

    YOURID  # 机器编号
    SLAVENUM  # 机器的数量
    """
          redis_event = RedisQueue(topic)
          down_username(SLAVENUM,YOURID,REDIS_EVENT)
          redis_event.del_key()   # master清空数据

# main_down_stats(1,1) #第一号机完成第一批任务。其他机子就修改这个参数就可以啦。!!
  • 再来看第二种方法——队列取出redis的数据
  • 思路很简单,就是很多机子排队,一人取一个数据,取完再排队。这样就可以多机子完成任务啦。

redis = RedisQueue('key')

while not redis.empty():
    try:
        username = redis.get()#调用方法,一次取出一个username,取出后这个元素就不存在redis了。直到把redis queue取空为止。
        main_headunter(username)#进行username爬虫
    expect:
        pass

这样,这个方法是全部分机对同一台机子进行操作,没有像第一方法那样分批任务,每台机子有自己对应的批。有理有弊,第一种方法是分工合作,第二方法是共同工作。

  • 最后结果返回:
    分布式的基本达到目的啦,但是每台机子处理的结果怎么办??肯定是要汇总的,那么我采用的是全部分机完成任务后scp 结果给master:
    注意:scp传输时候的问题可以参考我另一文章,例如传输的命令有空格问题
# coding=utf-8
import os
import re
def scp_to_master(file_name, YOURID):
    """
    上传对应文件夹下的txt到master
    :param file_name: txt所在的文件夹的目录
    :param YOURID: slave的编号
    :return:
    """
    stats_txt = './documents/topic/'+YOURID+'.txt'
    is_exists = os.path.exists(stats_txt)
    if not is_exists:
        pass
    else:
        print '上传....'
        scp_chmod = 'scp -p ' +'"' +stats_txt +'"' + ' [email protected]:' +'"/home/users/PycharmProjects/result/documents/topic1/'+ str1+'"'
        is_succeed = os.system(scp_chmod) # 传输成功返回大于0的数字
        if is_succeed:
            print "休眠5s, 再次尝试上传master"
            scp_to_master(file_name, YOURID)
        else:
            print "上传成功", scp_chmod

总结
总结:虽然是实现了分布式,但是对于优缺点还是要说一下的,有点就是针对于单机来说,这样的分布式肯定是比较快的,效率上时快很多的。缺点的话也很明显,首选全部任务都是放在master上,这样导致master的任务还是很重的,因为如果master出现问题,那么其他分机也是无法完成任务的,这就违背了分布式的基本原则了,没有真正意义上的减轻master的任务。然后在回收结果的时候采用的是scp的命令,这也是效率不高的一个错略,redis的分布式是可以直接在内部实现数据的共享的,但我没有学会。

写blog好久没写了,这次是为了留点笔记,和大家一起学习。

你可能感兴趣的:(redis,python,redis,分布式)