最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于redis樂觀鎖實(shí)現(xiàn)并發(fā)排隊(duì)

 更新時(shí)間:2022年12月25日 09:35:24   作者:野生大蝦  
這篇文章主要介紹了基于redis樂觀鎖實(shí)現(xiàn)并發(fā)排隊(duì)的相關(guān)資料,需要的朋友可以參考下

有個(gè)需求場(chǎng)景是這樣的,使用redis控制scrapy運(yùn)行的數(shù)量。當(dāng)系統(tǒng)的后臺(tái)設(shè)置為4時(shí),只允許scapry啟動(dòng)4個(gè)任務(wù),多余的任務(wù)則進(jìn)行排隊(duì)。

概況

最近做了一個(gè)django + scrapy + celery + redis 的爬蟲系統(tǒng),客戶購(gòu)買的主機(jī)除了跑其他程序外,還要跑我開發(fā)的這套程序,所以需要手動(dòng)控制scrapy的實(shí)例數(shù)量,避免過多的爬蟲給系統(tǒng)造成負(fù)擔(dān)。

流程設(shè)計(jì)

1、爬蟲任務(wù)由用戶以請(qǐng)求的方式發(fā)起,所有的用戶的請(qǐng)求統(tǒng)一進(jìn)入到celery進(jìn)行排隊(duì);
2、任務(wù)數(shù)量控制的執(zhí)行就交給reids,經(jīng)由celery保存到redis,包含了爬蟲啟動(dòng)所需要的必要信息,從redis取一條信息即可啟動(dòng)一個(gè)爬蟲;
3、通過scrapyd的接口來獲取當(dāng)前在運(yùn)行的爬蟲數(shù)量,以便決定下一步流程:如果小于4,則從redis中取相應(yīng)數(shù)量的信息來啟動(dòng)爬蟲,如果大于等于4,則繼續(xù)等待;
4、如果在運(yùn)行爬蟲的數(shù)量有所減少,則及時(shí)從reids中取相應(yīng)數(shù)量的信息來啟動(dòng)爬蟲。

代碼實(shí)現(xiàn)

業(yè)務(wù)代碼有點(diǎn)復(fù)雜和啰嗦,此處使用偽代碼來演示

import redis

# 實(shí)例化一個(gè)redis連接池
pool = redis.ConnectionPool(host='127.0.0.1', port=6379, decode_responses=True, db=4, password='')

r = redis.Redis(connection_pool=pool)
# 爬蟲實(shí)例限制為4 即只允許4個(gè)scrapy實(shí)例在運(yùn)行
limited = 4

# 聲明redis的樂觀鎖
lock = r.Lock()

# lock.acquire中有while循環(huán),即它會(huì)線程阻塞,直到當(dāng)前線程獲得redis的lock,才會(huì)繼續(xù)往下執(zhí)行代碼
if lock.acquire():
	# 1、從reids中取一條爬蟲信息
	info = redis.get() 
	
	# 2、while循環(huán)監(jiān)聽爬蟲運(yùn)行的數(shù)量
	while True:
		req = requests.get('http://127.0.0.1:6800/daemonstatus.json').json()
		# 統(tǒng)計(jì)當(dāng)前有多少個(gè)爬蟲在運(yùn)行
		running = req.get('running') + req.get('pending')
		
		# 3、判斷是否等待還是要增加爬蟲數(shù)量
		# 3.1 如果在運(yùn)行的數(shù)量大于等于設(shè)置到量 則繼續(xù)等待
		if running >= limited:
			continue
		
		# 3.2 如果小于 則啟動(dòng)爬蟲
		start_scrapy(info)
		# 3.3 將info從redis中刪除
		redis.delete(info)
		# 3.4 釋放鎖
		lock.release()
		break
		

當(dāng)前,這只是偽代碼而已,實(shí)際的業(yè)務(wù)邏輯可能是非常復(fù)雜的,如:

@shared_task
def scrapy_control(key_uuid):

    r = redis.Redis(connection_pool=pool)
    db = MysqlDB()
    speed_limited = db.fetch_config('REPTILE_SPEED')
    speed_limited = int(speed_limited[0])

    keywords_num = MysqlDB().fetch_config('SEARCH_RANDOM')
    keywords_num = int(keywords_num[0])


    # while True:
    lock = r.lock('lock')
    with open('log/celery/info.log', 'a') as f: f.write(str(datetime.datetime.now()) + '--' + str(key_uuid) + ' 進(jìn)入處理環(huán)節(jié)' +  '\n')
    try:
        # acquire默認(rèn)阻塞 如果獲取不到鎖時(shí) 會(huì)一直阻塞在這個(gè)函數(shù)的while循環(huán)中
        if lock.acquire():
            with open('log/celery/info.log', 'a') as f: f.write(str(datetime.datetime.now()) + '--' + str(key_uuid) + ' 獲得鎖' +  '\n')
            # 1 從redis中獲取信息
            redis_obj = json.loads(r.get(key_uuid))
            user_id = redis_obj.get('user_id')
            contents = redis_obj.get('contents')
            
            # 2 使用while循環(huán)處理核心邏輯          
            is_hold_print = True
            while True:
                req = requests.get('http://127.0.0.1:6800/daemonstatus.json').json()
                running = req.get('running') + req.get('pending')
                # 3 如果仍然有足夠的爬蟲在運(yùn)行 則hold住redis鎖,等待有空余的爬蟲位置讓出
                if running >= speed_limited:
                    if is_hold_print:
                        with open('log/celery/info.log', 'a') as f: f.write(str(datetime.datetime.now()) + '--' + str(key_uuid) + ' 爬蟲在運(yùn)行,線程等待中' +  '\n')
                        is_hold_print = False
                    time.sleep(1)
                    continue
                
                # 4 有空余的爬蟲位置 則往下走
                # 4.1 處理完所有的內(nèi)容后 釋放鎖
                if len(contents) == 0:
                    r.delete(key_uuid)
                    with open('log/celery/info.log', 'a') as f: f.write(str(datetime.datetime.now()) + '--' + str(key_uuid) + ' 任務(wù)已完成,從redis中刪除' +  '\n')
                    lock.release()
                    with open('log/celery/info.log', 'a') as f: f.write(str(datetime.datetime.now()) + '--' + str(key_uuid) + ' 釋放鎖' +  '\n')
                    break

                # 4.2 創(chuàng)建task任務(wù)
                task_uuid = str(uuid.uuid4())
                article_obj = contents.pop()
                article_id = article_obj.get('article_id')
                article = article_obj.get('content')
                try:
                    Task.objects.create(
                        task_uuid = task_uuid,
                        user_id = user_id,
                        article_id = article_id,
                        content = article
                    )
                except Exception as e:
                    with open('log/celery/error.log', 'a') as f: f.write(str(datetime.datetime.now()) + '--' + str(key_uuid) + '->' + str(task_uuid) + ' 創(chuàng)建Task出錯(cuò): ' + str(e) +  '\n')
                # finally:
                # 4.3 啟動(dòng)爬蟲任務(wù) 即便創(chuàng)建task失敗也會(huì)啟動(dòng)
                try:
                    task_chain(user_id, article, task_uuid, keywords_num)
                except Exception as e:
                    with open('log/celery/error.log', 'a') as f: f.write(str(datetime.datetime.now()) + '--' + str(key_uuid) + ' 啟動(dòng)任務(wù)鏈?zhǔn)? ' + str(e) +  '\n')
                
                # 加入sleep 防止代碼執(zhí)行速度快于爬蟲啟動(dòng)速度而導(dǎo)致當(dāng)前線程啟動(dòng)額外的爬蟲
                time.sleep(5)

    except Exception as e:
        with open('log/celery/error.log', 'a') as f: f.write(str(datetime.datetime.now()) + '--' + str(key_uuid) + ' 獲得鎖之后的操作出錯(cuò): ' + str(e) +  '\n')
        lock.release()

小坑
scrapy啟動(dòng)速度相對(duì)較慢,所以while循環(huán)中,代碼中執(zhí)行到了爬蟲的啟動(dòng),需要sleep一下再去通過scrapyd接口獲取爬蟲運(yùn)行的數(shù)量,如果立刻讀取,可能會(huì)造成誤判。

到此這篇關(guān)于基于redis樂觀鎖實(shí)現(xiàn)并發(fā)排隊(duì)的文章就介紹到這了,更多相關(guān)基于redis樂觀鎖實(shí)現(xiàn)并發(fā)排隊(duì)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • redis實(shí)現(xiàn)計(jì)數(shù)器-防止刷單方法介紹

    redis實(shí)現(xiàn)計(jì)數(shù)器-防止刷單方法介紹

    本文主要向大家介紹了redis實(shí)現(xiàn)計(jì)數(shù)器防止刷單的方法和有關(guān)代碼,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • Java Socket實(shí)現(xiàn)Redis客戶端的詳細(xì)說明

    Java Socket實(shí)現(xiàn)Redis客戶端的詳細(xì)說明

    socket編程是一門技術(shù),它主要是在網(wǎng)絡(luò)通信中經(jīng)常用到.這篇文章主要介紹了如何用Java Socket實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Redis客戶端,需要的朋友可以參考下
    2021-05-05
  • Redis 數(shù)據(jù)類型的詳解

    Redis 數(shù)據(jù)類型的詳解

    這篇文章主要介紹了Redis 數(shù)據(jù)類型的詳解的相關(guān)資料,支持五種數(shù)據(jù)類型,字符串,哈希,列表,集合及zset,需要的朋友可以參考下
    2017-08-08
  • Redis限流的幾種實(shí)現(xiàn)

    Redis限流的幾種實(shí)現(xiàn)

    面對(duì)越來越多的高并發(fā)場(chǎng)景,限流顯示的尤為重要,限流有許多種實(shí)現(xiàn)的方式,Redis具有很強(qiáng)大的功能,本文就詳細(xì)的介紹幾種方式,感興趣的可以了解一下
    2021-12-12
  • redis 查看所有的key方式

    redis 查看所有的key方式

    這篇文章主要介紹了redis 查看所有的key方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • redis服務(wù)器允許遠(yuǎn)程主機(jī)訪問的方法

    redis服務(wù)器允許遠(yuǎn)程主機(jī)訪問的方法

    今天小編就為大家分享一篇redis服務(wù)器允許遠(yuǎn)程主機(jī)訪問的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • 詳解緩存穿透擊穿雪崩解決方案

    詳解緩存穿透擊穿雪崩解決方案

    在我們?nèi)粘5拈_發(fā)中,有時(shí)需要系統(tǒng)在極短的時(shí)間內(nèi)完成成千上萬次的讀/寫操作,這個(gè)時(shí)候不是數(shù)據(jù)庫(kù)能夠承受的,通常會(huì)引入NoSQL技術(shù)。redis技術(shù)就是NoSQL技術(shù)中的一種,但是引入redis又有可能出現(xiàn)緩存穿透,緩存擊穿,緩存雪崩等問題。本文就對(duì)這三種問題進(jìn)行較深入剖析。
    2021-05-05
  • Redis實(shí)現(xiàn)延遲任務(wù)的常見方案詳解

    Redis實(shí)現(xiàn)延遲任務(wù)的常見方案詳解

    延遲任務(wù)(Delayed?Task)是指在未來的某個(gè)時(shí)間點(diǎn),執(zhí)行相應(yīng)的任務(wù),本文為大家整理了Redis實(shí)現(xiàn)延遲任務(wù)的幾個(gè)常見方案,希望對(duì)大家有所幫助
    2024-04-04
  • Redis中五種數(shù)據(jù)類型簡(jiǎn)單操作

    Redis中五種數(shù)據(jù)類型簡(jiǎn)單操作

    這篇文章主要介紹了Redis中五種數(shù)據(jù)類型簡(jiǎn)單操作的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • 基于Redis的分布式鎖的簡(jiǎn)單實(shí)現(xiàn)方法

    基于Redis的分布式鎖的簡(jiǎn)單實(shí)現(xiàn)方法

    這篇文章主要介紹了基于Redis的分布式鎖的簡(jiǎn)單實(shí)現(xiàn)方法,Redis官方給出兩種思路,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-10-10

最新評(píng)論

霍林郭勒市| 黄山市| 米脂县| 上虞市| 三都| 鄢陵县| 昌吉市| 自贡市| 南乐县| 临洮县| 宿松县| 大渡口区| 开化县| 昌宁县| 商都县| 德兴市| 噶尔县| 二连浩特市| 彭水| 岳西县| 莒南县| 镇坪县| 湘西| 汤阴县| 桐乡市| 鞍山市| 精河县| 海丰县| 弋阳县| 涿鹿县| 改则县| 涟源市| 德兴市| 白沙| 沅江市| 济阳县| 蕉岭县| 东乌珠穆沁旗| 府谷县| 耒阳市| 车险|