生产者与消费者不漏数据模式

 QUEUE 简单介绍:

queue 模块:
    queue 模块中有 Queue 类,LifoQueue、PriorityQueue 都继承了 Queue

maxsize:
    maxsize 是实例化 Queue 类时的一个参数,默认为 0  小于等于0时, 默认无限个
    Queue(maxsize=0) 可以控制队列中数据的容量

put : 
    Queue.put(block=True, timeout=None)

block:
    用于设置是否阻塞, timeout 用于设置阻塞时等待时长  默认阻塞
    
put_nowait() = put(block=False)  这个相当于不阻塞

阻塞 : 当队列满了之后,put 就会阻塞,一直等待队列不再满时向里面添加数据

不阻塞: 当队列满了之后,如果设置 put 不阻塞,或者等待时长到了之后会报错:queue.Full

get : Queue.get(block=True, timeout=None) 默认阻塞

get_nowait() = get(block=False) 这个相当于不阻塞

阻塞
    当队列空了之后,get 就会阻塞,一直等待队列中有数据后再获取数据

不阻塞
    当队列空了之后,如果设置 get 不阻塞,或者等待时长到了之后会报错:_queue.Empty
    
    
full & empty:
    Queue.empty()/Queue.full() 用于判断队列是否为空、满

尽量使用 qsize 代替

qsize:
    Queue.qsize() 用于获取队列中大致的数据量
        注意:在多线程的情况下不可靠 因为在获取 qsize 时,其他线程可能又对队列进行操作了

join
    join 会在队列存在未完成任务时阻塞,等待队列无未完成任务,需要配合 task_done 使用

task_done
    执行一次 put 会让未完成任务 +1 ,但是执行 get 并不会让未完成任务 -1 ,
    需要使用 task_done 让未完成任务 -1 ,否则 join 就无法判断
    队列为空时执行会报错:ValueError: task_done() called too many times
    
q = queue.Queue()    
q.qsize()           返回队列的大小  
q.empty()         如果队列为空,返回True,反之False  
q.full()          如果队列满了,返回True,反之False 
q.full              与 maxsize 大小对应  
q.get([block[, timeout]]) 获取队列,timeout等待时间  
q.get_nowait()         相当q.get(False) 
q.put(item)           写入队列,timeout等待时间  
q.put_nowait(item)    相当q.put(item, False) 
q.task_done()         在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号 
q.join()             实际上意味着等到队列为空,再执行别的操作
import os
import sys
import time
import pymysql
import traceback
import pandas as pd
import datetime

path = os.path.dirname(os.getcwd())
sys.path.append(path)

from db_config.sql_clause import SQL_CLAUSE
from spider.elm_real_time_data import main as elm_main
from spider.mt_real_time_data import main as mt_main
from log_config.log_setting import get_logger
from utils.proxy import get_ac_ip
from utils.proxies_mongo import get_random_ip
from config import MYSQL_CONFIG
from queue import Queue
from threading import Thread

logger = get_logger("custom_start_spider", "custom_start_spider")

connection = pymysql.connect(**MYSQL_CONFIG['myq_db'])


def producer(producer_queue):
    """
    :return:  创建生产者
    """
    hour = datetime.datetime.now().strftime('%H')
    day = datetime.datetime.now().strftime('%Y-%m-%d')

    # 11、12、13 正餐爬虫
    if 11 <= int(hour) < 14:
        get_data_sql = SQL_CLAUSE.get_data_sql2

    # 16、17、18、21、23 正餐 + 夜宵 爬虫
    else:
        get_data_sql = SQL_CLAUSE.get_data_sql3

    df = pd.read_sql(get_data_sql, connection)

    # sql1 = f'between "{day} {int(hour)}:00:00" and "{day} {int(hour) + 1}:00:00"'
    sql1 = f'> "{day} {int(hour)}:00:00"'  # sql 修改
    sql = SQL_CLAUSE.filter_sql.format(sql1=sql1)
    df2 = pd.read_sql(sql, connection)

    # 去重
    df = df[~df["shop_code"].isin(df2["shop_code"].tolist())]

    # 添加队列数据
    for index, row in df.iterrows():
        account = row.to_dict()
        producer_queue.put(account)

    producer_queue.join()  # 队列不消费完成, 会一直阻塞, 生产者线程不会结束


def consumer(producer_queue):
    """
    :param producer_queue: 队列
    :return: 消费者消费
    """
    # proxy = get_ac_ip()  # 芝麻 ip
    # proxy = None
    while True:
        account = producer_queue.get()
        try:
            plat = account.get("plat")  # 2 代表饿了么 3 代表美团

            if plat == "elm":
                proxy = get_random_ip("elm")
                result = elm_main(account, proxy)

            elif plat == "mt":
                proxy = get_random_ip("mt")
                result = mt_main(account, proxy)

            else:
                logger.info("---不存在---该平台")
                result = True
            logger.info(f"---------result---------:{result}" )
            if result is False:
                producer_queue.put(account)  # 未消费再次put进去
                logger.info("======未消费再次put=======")

            logger.info("------------spider end--------------")
        except Exception as e:
            logger.error(traceback.format_exc())
            logger.error(e)
            producer_queue.put(account)  # 未消费再次put进去
            logger.error("======出错---未消费再次put=======")

        producer_queue.task_done()  # 通知生产者,队列已消化完


def start_main_spider():
    start = time.time()
    queue = Queue()
    producer_thread = Thread(target=producer, args=(queue,))  # 生产者单线程
    producer_thread.start()

    for index in range(10):  # 消费者多线程
        consumer_thread = Thread(target=consumer, args=(queue,))
        consumer_thread.daemon = True  # 设置守护线程, 执行完毕后自己结束, 否则会一直僵死在这
        consumer_thread.start()

    producer_thread.join()  # 等待主线程结束
    end = time.time()
    print('总耗时:%s' % (end - start))


if __name__ == '__main__':
    start_main_spider()

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值