亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

最近忙于業(yè)務(wù)開發(fā)、交接和游戲,加上碰上了不定時(shí)出現(xiàn)的猶豫期和困惑期,荒廢學(xué)業(yè)了一段時(shí)間。天冷了,要重新拾起開始下階段的學(xué)習(xí)了。之前接觸到的一些數(shù)據(jù)搜索項(xiàng)目,涉及到請(qǐng)求模擬,基于反爬需要使用隨機(jī)的 User Agent ,于是使用 redis 實(shí)現(xiàn)了一個(gè)十分簡(jiǎn)易的 UA 池。

背景

最近的一個(gè)需求,有模擬請(qǐng)求的邏輯,要求每次請(qǐng)求的請(qǐng)求頭中的 User Agent 要滿足下面幾點(diǎn):

  • 每次獲取的 User Agent 是隨機(jī)的。
  • 每次獲取的 User Agent (短時(shí)間內(nèi))不能重復(fù)。
  • 每次獲取的 User Agent 必須帶有主流的操作系統(tǒng)信息(可以是 Uinux 、 windows 、 IOS 和Android/ target=_blank class=infotextkey>安卓等等)。

這里三點(diǎn)都可以從 UA 數(shù)據(jù)的來(lái)源解決,實(shí)際上我們應(yīng)該關(guān)注具體的實(shí)現(xiàn)方案。簡(jiǎn)單分析一下,流程如下:

使用Redis實(shí)現(xiàn)UA池

 

在設(shè)計(jì) UA 池的時(shí)候,它的數(shù)據(jù)結(jié)構(gòu)和環(huán)形隊(duì)列十分類似:

使用Redis實(shí)現(xiàn)UA池

 

上圖中,假設(shè)不同顏色的 UA 是完全不同的 UA ,它們通過(guò)洗牌算法打散放進(jìn)去環(huán)形隊(duì)列中,實(shí)際上每次取出一個(gè) UA 之后,只需要把游標(biāo) cursor 前進(jìn)或者后退一格即可(甚至可以把游標(biāo)設(shè)置到隊(duì)列中的任意元素)。最終的實(shí)現(xiàn)就是:需要通過(guò)中間件實(shí)現(xiàn)分布式隊(duì)列(只是隊(duì)列,不是消息隊(duì)列)。

具體實(shí)現(xiàn)方案

毫無(wú)疑問(wèn)需要一個(gè)分布式數(shù)據(jù)庫(kù)類型的中間件才能存放已經(jīng)準(zhǔn)備好的 UA ,第一印象就感覺(jué) Redis 會(huì)比較合適。接下來(lái)需要選用 Redis 的數(shù)據(jù)類型,主要考慮幾個(gè)方面:

UA

支持這幾個(gè)方面的 Redis 數(shù)據(jù)類型就是 List ,不過(guò)注意 List 本身不能去重,去重的工作可以用代碼邏輯實(shí)現(xiàn)。然后可以想象客戶端獲取 UA 的流程大致如下:

使用Redis實(shí)現(xiàn)UA池

 

結(jié)合前面的分析,編碼過(guò)程有如下幾步:

  1. 準(zhǔn)備好需要導(dǎo)入的 UA 數(shù)據(jù),可以從數(shù)據(jù)源讀取,也可以直接文件讀取。
  2. 因?yàn)樾枰獙?dǎo)入的 UA 數(shù)據(jù)集合一般不會(huì)太大,考慮先把這個(gè)集合的數(shù)據(jù)隨機(jī)打散,如果使用 JAVA 開發(fā)可以直接使用 Collections#shuffle() 洗牌算法,當(dāng)然也可以自行實(shí)現(xiàn)這個(gè)數(shù)據(jù)隨機(jī)分布的算法, 這一步對(duì)于一些被模擬方會(huì)嚴(yán)格檢驗(yàn) UA 合法性的場(chǎng)景是必須的 。
  3. 導(dǎo)入 UA 數(shù)據(jù)到 Redis 列表中。
  4. 編寫 RPOP + LPUSH 的 Lua 腳本,實(shí)現(xiàn)分布式循環(huán)隊(duì)列。

編碼和測(cè)試示例

引入 Redis 的高級(jí)客戶端 Lettuce 依賴:

<dependency>
 <groupId>io.lettuce</groupId>
 <artifactId>lettuce-core</artifactId>
 <version>5.2.1.RELEASE</version>
</dependency>

編寫 RPOP + LPUSH 的 Lua 腳本, Lua 腳本名字暫稱為 L_RPOP_LPUSH.lua ,放在 resources/scripts/lua 目錄下:

local key = KEYS[1]
local value = redis.call('RPOP', key)
redis.call('LPUSH', key, value)
return value

這個(gè)腳本十分簡(jiǎn)單,但是已經(jīng)實(shí)現(xiàn)了循環(huán)隊(duì)列的功能。剩下來(lái)的測(cè)試代碼如下:

public class UaPoolTest {

 private static RedisCommands<String, String> COMMANDS;

 private static AtomicReference<String> LUA_SHA = new AtomicReference<>();
 private static final String KEY = "UA_POOL";

 @BeforeClass
 public static void beforeClass() throws Exception {
 // 初始化Redis客戶端
 RedisURI uri = RedisURI.builder().withHost("localhost").withPort(6379).build();
 RedisClient redisClient = RedisClient.create(uri);
 StatefulRedisConnection<String, String> connect = redisClient.connect();
 COMMANDS = connect.sync();
 // 模擬構(gòu)建UA池的原始數(shù)據(jù),假設(shè)有10個(gè)UA,分別是UA-0 ... UA-9
 List<String> uaList = Lists.newArrayList();
 IntStream.range(0, 10).forEach(e -> uaList.add(String.format("UA-%d", e)));
 // 洗牌
 Collections.shuffle(uaList);
 // 加載Lua腳本
 ClassPathResource resource = new ClassPathResource("/scripts/lua/L_RPOP_LPUSH.lua");
 String content = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
 String sha = COMMANDS.scriptLoad(content);
 LUA_SHA.compareAndSet(null, sha);
 // Redis隊(duì)列中寫入U(xiǎn)A數(shù)據(jù),數(shù)據(jù)量多的時(shí)候可以考慮分批寫入防止長(zhǎng)時(shí)間阻塞Redis服務(wù)
 COMMANDS.lpush(KEY, uaList.toArray(new String[0]));
 }

 @AfterClass
 public static void afterClass() throws Exception {
 COMMANDS.del(KEY);
 }

 @Test
 public void testUaPool() {
 IntStream.range(1, 21).forEach(e -> {
 String result = COMMANDS.evalsha(LUA_SHA.get(), ScriptOutputType.VALUE, KEY);
 System.out.println(String.format("第%d次獲取到的UA是:%s", e, result));
 });
 }
}

某次運(yùn)行結(jié)果如下:

第1次獲取到的UA是:UA-0
第2次獲取到的UA是:UA-8
第3次獲取到的UA是:UA-2
第4次獲取到的UA是:UA-4
第5次獲取到的UA是:UA-7
第6次獲取到的UA是:UA-5
第7次獲取到的UA是:UA-1
第8次獲取到的UA是:UA-3
第9次獲取到的UA是:UA-6
第10次獲取到的UA是:UA-9
第11次獲取到的UA是:UA-0
第12次獲取到的UA是:UA-8
第13次獲取到的UA是:UA-2
第14次獲取到的UA是:UA-4
第15次獲取到的UA是:UA-7
第16次獲取到的UA是:UA-5
第17次獲取到的UA是:UA-1
第18次獲取到的UA是:UA-3
第19次獲取到的UA是:UA-6
第20次獲取到的UA是:UA-9

可見洗牌算法的效果不差,數(shù)據(jù)相對(duì)分散。

小結(jié)

其實(shí) UA 池的設(shè)計(jì)難度并不大,需要注意幾個(gè)要點(diǎn):

  • 一般主流的移動(dòng)設(shè)備或者桌面設(shè)備的系統(tǒng)版本不會(huì)太多,所以來(lái)源 UA 數(shù)據(jù)不會(huì)太多,最簡(jiǎn)單的實(shí)現(xiàn)可以使用文件存放,一次讀取直接寫入 Redis 中。
  • 注意需要隨機(jī)打散 UA 數(shù)據(jù),避免同一個(gè)設(shè)備系統(tǒng)類型的 UA 數(shù)據(jù)過(guò)于密集,這樣可以避免觸發(fā)模擬某些請(qǐng)求時(shí)候的風(fēng)控規(guī)則。
  • 需要熟悉 Lua 的語(yǔ)法,畢竟 Redis 的原子指令一定離不開 Lua 腳本。

原文鏈接:http://www.throwable.club/2019/11/14/redis-in-action-ua-pool/

分享到:
標(biāo)簽:Redis
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定