新聞資訊  快訊  焦點  財經  政策  社會
互 聯 網   電商  金融  數據  計算  技巧
生活百科  科技  職場  健康  法律  汽車
手機百科  知識  軟件  修理  測評  微信
軟件技術  應用  系統  圖像  視頻  經驗
硬件技術  知識  技術  測評  選購  維修
網絡技術  硬件  軟件  設置  安全  技術
程序開發  語言  移動  數據  開源  百科
安全防護  資訊  黑客  木馬  病毒  移動
站長技術  搜索  SEO  推廣  媒體  移動
財經百科  股票  知識  理財  財務  金融
教育考試  育兒  小學  高考  考研  留學
您當前的位置:首頁 > IT百科 > 數據庫 > Redis

使用Redis實現UA池

時間:2019-11-22 15:54:22  來源:  作者:

最近忙于業務開發、交接和游戲,加上碰上了不定時出現的猶豫期和困惑期,荒廢學業了一段時間。天冷了,要重新拾起開始下階段的學習了。之前接觸到的一些數據搜索項目,涉及到請求模擬,基于反爬需要使用隨機的 User Agent ,于是使用 redis 實現了一個十分簡易的 UA 池。

背景

最近的一個需求,有模擬請求的邏輯,要求每次請求的請求頭中的 User Agent 要滿足下面幾點:

  • 每次獲取的 User Agent 是隨機的。
  • 每次獲取的 User Agent (短時間內)不能重復。
  • 每次獲取的 User Agent 必須帶有主流的操作系統信息(可以是 Uinux 、 windowsIOSAndroid/ target=_blank class=infotextkey>安卓等等)。

這里三點都可以從 UA 數據的來源解決,實際上我們應該關注具體的實現方案。簡單分析一下,流程如下:

使用Redis實現UA池

 

設計 UA 池的時候,它的數據結構和環形隊列十分類似:

使用Redis實現UA池

 

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

具體實現方案

毫無疑問需要一個分布式數據庫類型的中間件才能存放已經準備好的 UA ,第一印象就感覺 Redis 會比較合適。接下來需要選用 Redis 的數據類型,主要考慮幾個方面:

UA

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

使用Redis實現UA池

 

結合前面的分析,編碼過程有如下幾步:

  1. 準備好需要導入的 UA 數據,可以從數據源讀取,也可以直接文件讀取。
  2. 因為需要導入的 UA 數據集合一般不會太大,考慮先把這個集合的數據隨機打散,如果使用 JAVA 開發可以直接使用 Collections#shuffle() 洗牌算法,當然也可以自行實現這個數據隨機分布的算法, 這一步對于一些被模擬方會嚴格檢驗 UA 合法性的場景是必須的 。
  3. 導入 UA 數據到 Redis 列表中。
  4. 編寫 RPOP + LPUSH 的 Lua 腳本,實現分布式循環隊列。

編碼和測試示例

引入 Redis 的高級客戶端 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

這個腳本十分簡單,但是已經實現了循環隊列的功能。剩下來的測試代碼如下:

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();
 // 模擬構建UA池的原始數據,假設有10個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隊列中寫入UA數據,數據量多的時候可以考慮分批寫入防止長時間阻塞Redis服務
 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));
 });
 }
}

某次運行結果如下:

第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

可見洗牌算法的效果不差,數據相對分散。

小結

其實 UA 池的設計難度并不大,需要注意幾個要點:

  • 一般主流的移動設備或者桌面設備的系統版本不會太多,所以來源 UA 數據不會太多,最簡單的實現可以使用文件存放,一次讀取直接寫入 Redis 中。
  • 注意需要隨機打散 UA 數據,避免同一個設備系統類型的 UA 數據過于密集,這樣可以避免觸發模擬某些請求時候的風控規則。
  • 需要熟悉 Lua 的語法,畢竟 Redis 的原子指令一定離不開 Lua 腳本。

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



Tags:Redis   點擊:()  評論:()
聲明:本站部分內容來自互聯網,內容觀點僅代表作者本人,如有任何版權侵犯請與我們聯系,我們將立即刪除。
▌相關評論
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
▌相關推薦
Redis沒有直接使用C語言傳統的字符串表示,而是自己構建了一種名為簡單動態字符串(simple dynamic string, SDS)的抽象類型,并將SDS用作Redis的默認字符串表示。...【詳細內容】
2019-12-26   Redis  點擊:(1)  評論:(0)  加入收藏
單機的 redis,能夠承載的 QPS 大概就在上萬到幾萬不等。對于緩存來說,一般都是用來支撐讀高并發的。因此架構做成主從(master-slave)架構,一主多從,主負責寫,并且將數據復制到其...【詳細內容】
2019-12-26   Redis  點擊:(1)  評論:(0)  加入收藏
連接數據庫$redis = new Redis();$redis->connect(&#39;127.0.0.1&#39;,6379);//鏈接redis服務// 參數// `host: string`,服務地址// `port: int`,端口號// `timeout: float`...【詳細內容】
2019-12-25   Redis  點擊:(3)  評論:(0)  加入收藏
添加maven依賴,使用springboot2.x版本 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependenc...【詳細內容】
2019-09-26   Redis  點擊:(1)  評論:(0)  加入收藏
Redis的 list 數據結構常用來作為 異步消息隊列 使用,使用 rpush/lpush 操作 入隊 ,使用 lpop/rpop 來操作 出隊 > rpush my-queue apple banana pear(integer) 3> llen my-qu...【詳細內容】
2019-12-25   Redis  點擊:(1)  評論:(0)  加入收藏
作者:GrimMjx目錄 開發語言 純內存訪問 單線程 非阻塞多路I/O復用機制前言Redis是一種基于鍵值對(Key-Value)的NoSQL數據庫,Redis的Value可以由String,hash,list,set,zset,Bitmaps...【詳細內容】
2019-12-19   Redis  點擊:(1)  評論:(0)  加入收藏
前言Redis 是如今互聯網技術架構中,使用最廣泛的緩存。支持復雜的數據結構,支持持久化,支持主從集群,支持高可用,支持較大的value存儲... 同時, Redis 也是中高級后端工程師技術面...【詳細內容】
2019-12-25   Redis  點擊:(5)  評論:(0)  加入收藏
為什么要在 Java 分布式應用程序中使用緩存?在提高應用程序速度和性能上,每一毫秒都很重要。根據谷歌的一項研究,假如一個網站在3秒鐘或更短時間內沒有加載成功,會有 53% 的手機用戶會離開。...【詳細內容】
2019-12-23   Redis  點擊:(1)  評論:(0)  加入收藏
一、Redis雪崩、穿透、并發等5大難題解決方案緩存雪崩數據未加載到緩存中,或者緩存同一時間大面積的失效,從而導致所有請求都去查數據庫,導致數據庫CPU和內存負載過高,甚至宕機...【詳細內容】
2019-12-19   Redis  點擊:(13)  評論:(0)  加入收藏
本篇主要講述如何使用基本的注解 @Cacheable @CachePut @CacheEvict 操作緩存1.我們導入Redis的依賴<!--這里Redis我給了版本--> <dependency> <groupId>org.springframewor...【詳細內容】
2019-12-18   Redis  點擊:(9)  評論:(0)  加入收藏
知識點 基于 Server-Sent Event 工作方式,Web 即時通信 Redis 包 發布訂閱功能的使用 flask 快速入門,常用對象實例方法函數 Vuejs 列表頁面自動渲染效果圖 代碼段 app.py 主...【詳細內容】
2019-12-13   Redis  點擊:(11)  評論:(0)  加入收藏
之前本人在找工作面試時在Redis相關問題上可栽了跟頭。在面試前按常規套路準備了一下,比如 Redis 的常用5種數據結構,Redis持久化策略,Redis實現分布式鎖,簡單發布訂閱等等都準...【詳細內容】
2019-12-12   Redis  點擊:(10)  評論:(0)  加入收藏
Redis在國內各大公司都很熱門,比如新浪、阿里、騰訊、百度、美團、小米等。Redis也是大廠面試最愛問的,尤其是Redis客戶端、Redis高級功能、Redis持久化和開發運維常用問題探...【詳細內容】
2019-12-11   Redis  點擊:(16)  評論:(0)  加入收藏
前言當結束Java和數據庫的學習以后,你就會接觸到Redis這個詞,我第一次聽到的時候腦海里就會浮現這兩個問題:什么是Redis?為什么我們要用Redis?我了解完以后,寫出來幫助大家能夠更...【詳細內容】
2019-12-10   Redis  點擊:(12)  評論:(0)  加入收藏
Redis是一個開源的使用ANSI C語言編寫、支持網絡、可基于內存亦可持久化的日志型、Key-Value數據庫,并提供多種語言的API。Redis是一個高性能的key-value數據庫。Redis的出現...【詳細內容】
2019-12-10   Redis  點擊:(15)  評論:(0)  加入收藏
SCAN cursor [MATCH pattern] [COUNT count]SCAN 命令及其相關的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代(incrementally iterate)一集元素(a collection of...【詳細內容】
2019-12-09   Redis  點擊:(13)  評論:(0)  加入收藏
redis事務任何數據庫都要有一套自己的事務控制機制,redis事務是一次可以執行多個命令,它的本質是一組命令的集合。一個事務中所有的命令都會被序列化,在事務執行的過程中會按照...【詳細內容】
2019-07-16   Redis  點擊:(10)  評論:(0)  加入收藏
一、Sentinel介紹Sentinel是Redis的高可用性(HA)解決方案: 由一個或多個Sentinel實例組成的Sentinel系統可以監視任意多個主服務器,以及這些主服務器屬下的所有從服務器,并在被...【詳細內容】
2019-12-06   Redis  點擊:(17)  評論:(0)  加入收藏
key 操作 刪除 key:del key 批量刪除key:redis-cli -a(密碼)keys “QXJ_*”| xargs redis-cli -a(密碼)del 查看所有的 key(一次性遍歷整個數據庫,生產上慎重使用):keys [pattern] 利...【詳細內容】
2019-12-04   Redis  點擊:(14)  評論:(0)  加入收藏
Redis 是內存數據庫,數據都是存儲在內存中,為了避免進程退出導致數據的永久丟失,需要定期將 Redis 中的數據以數據或命令的形式從內存保存到本地磁盤。當下次 Redis 重啟時,利用...【詳細內容】
2019-12-02   Redis  點擊:(15)  評論:(0)  加入收藏
最新更新
欄目熱門
欄目頭條
31选7开奖11185