可恶的爬虫直接把生产6台机器爬挂了!

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 可恶的爬虫直接把生产6台机器爬挂了!

引言

  • 正在午睡,突然收到线上疯狂报警的邮件,查看这个邮件发现这个报警的应用最近半个月都没有发布,应该不至于会有报警,但是还是打开邮件通过监控发现是由于某个接口某个接口流量暴增,CPU暴涨。为了先解决问题只能先暂时扩容机器了,把机器扩容了一倍,问题得到暂时的解决。最后复盘为什么流量暴增?由于最近新上线了一个商品列表查询接口,主要用来查询商品信息,展示给到用户。业务逻辑也比较简单,直接调用底层一个soa接口,然后把数据进行整合过滤,排序推荐啥的,然后吐给前端。这个接口平时流量都很平稳。线上只部署了6台机器,面对这骤增的流量,只能进行疯狂的扩容来解决这个问题。扩容机器后一下问题得到暂时的解决。后来经过请求分析原来大批的请求都是无效的,都是爬虫过来爬取信息的。这个接口当时上线的时候是裸着上的也没有考虑到会有爬虫过来。

解决办法

  • 既然是爬虫那就只能通过反爬来解决了。自己写一套反爬虫系统,根据用户的习惯,请求特征啥的,浏览器cookie、同一个请求频率、用户ID、以及用户注册时间等来实现一个反爬系统。
  • 直接接入公司现有的反爬系统,需要按照它提供的文档来提供指定的格式请求日志让它来分析。

既然能够直接用现成的,又何必自己重新造轮子呢。最后决定还是采用接入反爬系统的爬虫组件。爬虫系统提供了两种方案如下:

方案1:

  • 爬虫系统提供批量获取黑名单IP的接口(getBlackIpList)和移除黑名单IP接口(removeBlackIp)。

业务项目启动的时候,调用getBlackIpList接口把所有IP黑名单全部存入到本地的一个容器里面(Map、List),中间会有一个定时任务去调用getBlackIpList接口全量拉取黑名单(黑名单会实时更新,可能新增,也可能减少)来更新这个容器。

  • 每次来一个请求先经过这个本地的黑名单IP池子,IP是否在这个池子里面,如果在这个池子直接返回爬虫错误码,然后让前端弹出一个复杂的图形验证码,如果用户输入验证码成功(爬虫基本不会去输入验证码),然后把IP从本地容器移除,同时发起一个异步请求调用移除黑名单IP接口(removeBlackIp),以防下次批量拉取黑单的时候又拉入进来了。然后在发送一个activemq消息告诉其他机器这个IP是被误杀的黑名单,其他机器接受到了这个消息也就会把自己容器里面这个IP移除掉。(其实同步通知其他机器也可以通过把这个IP存入redis里面,如果在命中容器里面是黑名单的时候,再去redis里面判断这个ip是否存在redis里面,如果存在则说明这个ip是被误杀的,应该是正常请求,下次通过定时任务批量拉取黑名单的时候,拉取完之后把这个redis里面的数据全部删除,或者让它自然过期。

这种方案:性能较好,基本都是操作本地内存。但是实现有点麻烦,要维护一份IP黑名单放在业务系统中。
在这里插入图片描述

方案2:

  • 爬虫系统提供单个判断IP是否黑名单接口checkIpIsBlack(但是接口耗时有点长5s)和移除黑名单IP接口(removeBlackIp)。每一个请求过来都去调用爬虫系统提供的接口(判断IP是否在黑名单里面)这里有一个网络请求会有点耗时。如果爬虫系统返回是黑名单,就返回一个特殊的错误码给到前端,然后前端弹出一个图形验证码,如果输入的验证码正确,则调用爬虫系统提供的移除IP黑名单接口,把IP移除。

这种方案:对于业务系统使用起来比较简单,直接调用接口就好,没有业务逻辑,但是这个接口耗时是没法忍受的,严重影响用户的体验
最终综合考虑下来最后决定采用方案1.毕竟系统对响应时间是有要求的尽量不要增加不必要的耗时。

方案1 实现

方案1伪代码实现 我们上文《看了CopyOnWriteArrayList后自己实现了一个CopyOnWriteHashMap》有提到过对于读多写少的线程安全的容器我们可以选择CopyOnWrite容器。

static CopyOnWriteArraySet blackIpCopyOnWriteArraySet = null;
    /**
     * 初始化
     */
    @PostConstruct
    public void init() {
        // 调用反爬系统接口 拉取批量黑名单
        List<String> blackIpList = getBlackIpList();
        // 初始化
        blackIpCopyOnWriteArraySet = new CopyOnWriteArraySet(blackIpList);
    }

    /**
     * 判断IP 是否黑名单
     * @param ip
     * @return
     */
    public boolean checkIpIsBlack(String ip) {
      boolean checkIpIsBlack =  blackIpCopyOnWriteArraySet.contains(ip);
       if (!checkIpIsBlack ) 
               return false;
       // 不在redis白名单里面
       if (!RedisUtils.exist(String.format("whiteIp_%", ip)){
               return false;
        } 
       return  true;
    }

上线后经过一段时间让爬虫系统消费我们的请求日志,经过一定模型特征的训练,效果还是很明显的。由于大部分都是爬虫很多请求直接就被拦截了,所以线上的机器可以直接缩容掉一部分了又回到了6台。但是好景不长,突然发现GC次数频繁告警不断。为了暂时解决问题,赶紧把生产机器进行重启(生产出问题之后,除了重启和回退还有什么解决办法吗),并且保留了一台机器把它拉出集群,重启之后发现过又是一样的还是没啥效果。通过dump线上的一台机器,通过MemoryAnalyzer分析发现一个大对象就是我们存放IP的大对象,存放了大量的的IP数量。这个IP存放的黑名单是放在一个全局的静态CopyOnWriteArraySet,所以每次gc 它都不会被回收掉。只能临时把线上的机器配置都进行升级,由原来的8核16g直接变为16核32g,新机器上线后效果很显著。
为啥测试环境没有复现?
测试环境本来就没有什么其他请求,都是内网IP,几个黑名单IP还是开发手动构造的。

解决方案

业务系统不再维护IP黑名单池子了,由于黑名单来自反爬系统,爬虫黑名单的数量不确定。所以最后决定采取方案2和方案1结合优化。

  • 1.项目启动的时候把所有的IP黑名单全部初始化到一个全局的布隆过滤器
  • 2.一个请求过来先经过布隆过滤器,判断是否在布隆过滤器里面,如果在的话我们再去看看是否在redis白名单里面(误杀用户需要进行洗白)我们再去请求反爬系统判断IP是否是黑名单接口,如果接口返回是IP黑名单直接返回错误码给到前端,如果不是直接放行(布隆过滤器有一定的误判,但是误判率是非常小的,所以即使被误判了,最后再去实际请求接口,这样的话就不会存在真正的误判真实用户)。如果不存在布隆器直接放行。
  • 3.如果是被误杀的用户,用户进行了IP洗白,布隆过滤器的数据是不支持删除(布谷鸟布隆器可以删除(可能误删)),把用户进行正确洗白后的IP存入redis里面。(或者一个本地全局容器,mq消息同步其他机器)

下面我们先来了解下什么是布隆过滤器把。

什么是布隆过滤器
布隆过滤器(英语:Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

上述出自百度百科。
说白了布隆过滤器主要用来判断一个元素是否在一个集合中,它可以使用一个位数组简洁的表示一个数组。它的空间效率和查询时间远远超过一般的算法,不过它存在一定的误判的概率,适用于容忍误判的场景。如果布隆过滤器判断元素存在于一个集合中,那么大概率是存在在集合中,如果它判断元素不存在一个集合中,那么一定不存在于集合中。

实现原理

 布隆过滤器的原理是,当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组(Bit array)中的 K 个点,把它们置为 1 。检索时,只要看看这些点是不是都是1就知道元素是否在集合中;如果这些点有任何一个 0,则被检元素一定不在;如果都是1,则被检元素很可能在(之所以说“可能”是误差的存在)。底层是采用一个bit数组和几个哈希函数来实现。
在这里插入图片描述
在这里插入图片描述
下面我们以一个 bloom filter 插入"java" 和"PHP"为例,每次插入一个元素都进行了三次hash函数
java第一次hash函数得到下标是2,所以把数组下标是2给置为1
java第二次Hash函数得到下标是3,所以把数组下标是3给置为1
java第三次Hash函数得到下标是5,所以把数组下标是5给置为1
PHP 第一次Hash函数得到下标是5,所以把数组下标是5给置为1
...
查找的时候,当我们去查找C++的时候发现第三次hash位置为0,所以C++一定是不在不隆过滤器里面。但是我们去查找“java”这个元素三次hash出来对应的点都是1。只能说这个元素是可能存在集合里面。

  • 布隆过滤器添加元素
  1. 将要添加的元素给k个哈希函数
  2. 得到对应于位数组上的k个位置
  3. 将这k个位置设为1
  • 布隆过滤器查询元素
  1. 将要查询的元素给k个哈希函数
  2. 得到对应于位数组上的k个位置
  3. 如果k个位置有一个为0,则肯定不在集合中
  4. 如果k个位置全部为1,则可能在集合中

使用BloomFilter

引入pom

 <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
  </dependency> 
    public static int count = 1000000;
    private static BloomFilter<String> bf = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), count,0.009);
    public static void main(String[] args) {
        int missCount = 0;
        for (int i = 0; i < count; i++) {
            bf.put(i+"");
        }
        for (int i = count; i < count+1000000; i++) {
            boolean b = bf.mightContain(i +"");
            if (b) {
                missCount++;
            }
        }
        System.out.println(new BigDecimal(missCount).divide(new BigDecimal(count)));
    }

解决问题

布隆过滤器介绍完了,我们再回到上述的问题,我们把上述问题通过伪代码来实现下;

   /**
     * 初始化
     */
    @PostConstruct
    public void init() {
        // 这个可以通过配置中心来读取
        double fpp = 0.001;
        // 调用反爬系统接口 拉取批量黑名单
        List<String> blackIpList = getBlackIpList();
        // 初始化 不隆过滤器
        blackIpBloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), blackIpList.size(), fpp);
        for (String ip: blackIpList) {
            blackIpBloomFilter.put(ip);
        }
    }
    /**
     * 判断是否是爬虫
     */
    public boolean checkIpIsBlack(String ip) {
        boolean contain = blackIpBloomFilter.mightContain(ip);
        if (!contain) {
            return false;
        }
         // 不在redis白名单里面
       if (!RedisUtils.exist(String.format("whiteIp_%", ip)){
               return false;
        } 
        // 调用反爬系统接口 判断IP是否在黑名单里面
    }

总结

上述只是列举了通过IP来反爬虫,这种反爬的话只能应对比较低级的爬虫,如果稍微高级一点的爬虫也可以通过代理IP来继续爬你的网站,这样的话成本可能就会加大了一点。爬虫虽然好,但是还是不要乱爬,“爬虫爬的好,牢饭吃到饱

结束

  • 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
  • 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
  • 感谢您的阅读,十分欢迎并感谢您的关注。
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
5月前
|
数据采集
爬虫抓取数据过程中代理IP频繁掉线:以Kookeey为例的解决方案
在数据抓取过程中,代理IP不可或缺,但其频繁掉线问题严重影响效率。本文以Kookeey为例,探讨解决之道:1)深入理解掉线原因,审慎选择稳定的服务商; 2)保持代理IP更新并合理配置使用参数; 3)在程序中集成错误处理与重试逻辑; 4)必要时升级至更优质的服务; 5)主动与服务商沟通寻求支持; 6)探索替代抓取策略,如调整抓取节奏或采用分布式架构。这些措施有助于显著提升抓取成功率及稳定性。
|
5月前
|
监控 Linux Shell
"揭秘!一键掌控Linux服务器健康的秘密武器——超实用系统检查脚本,让你的服务器稳如老狗,告别宕机烦恼!"
【8月更文挑战第14天】服务器宕机或资源耗尽会严重影响业务。为此,你需要一个Linux系统检查脚本来守护服务器健康。它可以自动检测潜在问题如磁盘满载、内存泄漏等,避免服务中断。脚本应包括磁盘空间、内存/CPU使用、系统时间准确性、关键服务状态及系统日志分析等检查项。通过编写并定期运行这样的脚本,可以显著提高服务器的稳定性和可靠性。
66 1
|
Web App开发 Rust 安全
解雇拒绝打开摄像头员工被罚 51 万;推送损坏的更新,导致数千网站瘫痪;PHP 存在不受控制的递归漏洞|思否周刊
解雇拒绝打开摄像头员工被罚 51 万;推送损坏的更新,导致数千网站瘫痪;PHP 存在不受控制的递归漏洞|思否周刊
147 0
|
消息中间件 分布式计算 监控
RabbitMQ 线上事故!慌的一批,脑袋一片空白
1.什么是kafka Kafka是分布式发布-订阅消息系统,它最初是由LinkedIn公司开发的,之后成为Apache项目的一部分,Kafka是一个分布式,可划分的,冗余备份的持久性的日志服务,它主要用于处理流式数据。 2.为什么要使用 kafka,为什么要使用消息队列 缓冲和削峰: 上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,kafka在中间可以起到一个缓冲的作用,把消息暂存在kafka中,下游服务就可以按照自己的节奏进行慢慢处理。 解耦和扩展性: 项目开始的时候,并不能确定具体需求。消息队列可以作为一个接口层,解耦重要的业务流程。只需要遵守约定,针对数据
RabbitMQ 线上事故!慌的一批,脑袋一片空白
国外隔离迷惑行为:这个网站帮你复现办公室白噪音,还能自行决定同事数量
国外隔离迷惑行为:这个网站帮你复现办公室白噪音,还能自行决定同事数量
157 0
|
安全 Java Apache
早上收到这样一份通知,求一无漏洞框架,无力吐槽
关于停止使用Apache Struts2开发框架的通知   各有关单位: Apache Struts2(以下简称“S2”)是一种开源的、基于MVC架构的Java Web应用开发框架。S2自从2007年面世以来被国内外广泛使用,但也因屡次被披露存在高风险漏洞而闻名。
1199 0
|
CDN
为啥我的网站服务器访问速度慢?网站都没人看了
载入速度对于网站、游戏、APP、视频等等来说,可以说是生死存亡的关键,如果长期访问速度不稳定,会造成客户流失、效益受损,甚至导致整体形象的损坏。而访问速度的快慢是与多方面的原因相关,我们今天可以大致了解下:
658 0
|
安全
警惕“搜房网”“凯业房园”等网站被挂马
据瑞星“云安全”系统统计, 上周末期间,共有5,556,431人次的网民遭到网页挂马攻击,瑞星共截获了748,911个挂马网址。 周末最流行木马病毒是 Worm.Win32.Autorun.gbn(U盘蠕虫下载器)“云安全”系统共收到100,754次用户上报。
1817 0
|
安全
未找到工作先遇黑客 八大人事局网站被挂马
本想去当地的人事局网站看看就业信息,没想到一打开竟然全是淫秽网站。家住开封的大四学生小李近日遇上了不堪的事。原来黑客打起了就业人群的主意,斗胆在开封人事局网站(http://www.kfrs.gov.cn/inc/)上做起“有色”买卖。
1150 0