您好,登录后才能下订单哦!
这篇文章将为大家详细讲解有关Crawlab的核心原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
对于一般的爬虫爱好者来说,写一个单机爬虫脚本已经足够,而且Scrapy这样的优秀爬虫框架能够让开发者轻松调试、编写一个简单的爬虫,需要定时任务就直接上Crontab,分分钟搞定。然而,一般的企业对爬虫的要求相对较高,其中主要涉及一个问题,也就是规模(Scale)。当然,这里的规模是指大型规模。爬虫的规模分为两种:一种是爬虫需要抓取大量的数据(Volume),例如全网抓取淘宝的商品;另一种是爬虫需要涵盖大量的网站(Coverage),例如搜索引擎。
不同的规模需要有不同的架构策略(如下图):
当网站数量只有一个,抓取结果不多时,只需要单机即可,并不需要分布式爬虫;
但当需要抓取结果量级提升时,例如全网抓取淘宝,就需要用分布式爬虫了,这是因为单个机器的带宽和计算资源不足以做到全网抓取,而且为了应对反爬虫技术,还需要大量的代理IP;
同理,当需要抓取网站的数量增多时,例如你需要创建一个新闻搜索引擎,你同样需要多台机器来获取足够的带宽和计算资源;
对于同时要求Volume和Coverage的应用,不是一般的小企业或个人能做的,对不管是人力和机器的资源都非常高。
而爬虫管理平台就是针对情况(2)、(3)、(4)而存在的分布式管理平台,能够让用户轻松管理多个爬虫或多机运行的爬虫。
Crawlab从诞生之初就解决了分布式爬虫问题,最早采用了Celery作为分布式任务调度引擎,以Redis作为消息队列,HTTP请求作为节点通信媒介,简单地实现了分布式管理。但随着用户不断使用Crawlab,发现这样的方式并不是很方便,用户需要指定节点的IP地址和API端口,而且还不能指定节点执行任务。因为各种问题,在最新版本v0.3.0
用Golang重构后端的时候,就将Celery弃用了,转而自己开发分布式节点的监控和通信应用,这样更加灵活和高效。本文是核心原理介绍,下面将着重介绍Crawlab的分布式架构原理(Golang版本)。
Crawlab的整体架构如下图,由五大部分组成:
主节点(Master Node):负责任务派发、API、部署爬虫等;
工作节点(Worker Node):负责执行爬虫任务;
MongoDB数据库:存储节点、爬虫、任务等日常运行数据;
Redis数据库:储存任务消息队列、节点心跳等信息。
前端客户端:Vue应用,负责前端交互和向后端请求数据。
以执行爬虫任务为例,它是Crawlab中常见的使用场景,我们来看看它具体是如何工作的:
前端向主节点发起请求,要求指定在某一工作节点执行任务;
主节点收到该请求,并将任务数据推送到Redis任务队列中;
工作节点持续监听Redis任务队列,并利用LPOP获取任务;
工作节点执行任务,并将结果写回到储存数据库;
以上就是执行爬虫任务的大致流程。当然,这还不是全部,我们还需要考虑日志处理、并发执行、取消任务等细节问题。具体的处理信息,请查看相关文档和源代码。
总的来说,可以将主节点看作是Crawlab整体架构的中控系统,理解为Crawlab的大脑;工作节点是实际干活的部分,是Crawlab的运动躯体;MongoDB和Redis是负责通信交流的,可以看作Crawlab的血液和神经网络。这些模块一起构成了一个完整、自洽、相互协作的系统。
节点监控主要是通过Redis来完成的(如下图)。
工作节点会不断更新心跳信息在Redis上,利用HSET nodes <node_id> <msg>
,心跳信息<msg>
包含节点MAC地址,IP地址,当前时间戳。
主节点会周期性获取Redis上的工作节点心跳信息。如果有工作节点的时间戳在60秒之前,则考虑该节点为离线状态,会在Redis中删除该节点的信息,并在MongoDB中设置为"离线";如果时间戳在过去60秒之内,则保留该节点信息,在MongoDB中设置为"在线"。
这样,就做到了一个监控节点是否在线的节点注册系统。这样架构的好处在于,节点之间根本不用像HTTP、RPC那样IP或端口,只需要知道Redis的地址就可以完成节点注册和监控。因此,也就减少了用户配置节点的操作,简化了使用流程。同时,由于隐藏了IP地址和端口,也更为安全。另外,相较于Celery版本的监控,我们去除了Flower服务,不用在服务中单独起一个Flower服务的进程,减少了开销。
下图是Crawlab UI界面上的节点之间的关系图(拓扑图)。
相较于一些常见的分布式架构,例如Zookeeper,Crawlab还存在一些不完善的地方。
高可用性(High Availability)是Crawlab暂时还没有做得很好的。例如,当主节点宕机的时候,整个系统就会瘫痪,因为主节点是Crawlab的大脑中枢,负责很多功能。如果主节点宕机,前端就无法获取API数据,任务无法调度,当然也无法监控节点了。虽然Zookeeper没有将可用性(Availability)做得非常完善,但其投票选举机制保证了其一定程度的高可用。如果Crawlab要改善这一点的话,会在主节点宕机后,用一定的方式选举出另一个主节点,保证高可用。
如果仔细看上面的整体架构图的话,你可能会注意到Crawlab中通信有两种。一种是同步信息(Sync via Msg),另一种是派发任务(Assign Tasks)。这两种通信分别叫即时通信和延迟通信。下面分别介绍。
即时通信是指某节点A通过某种媒介向另一节点B发送信息,取决于是否为双向通信,节点B收到信息后可能会通过同一种媒介将信息回复给节点A。
Crawlab的即时通信是通过Redis的PubSub来实现的(如下图)。
所谓PubSub,简单来说是一个发布订阅模式。订阅者(Subscriber)会在Redis上订阅(Subscribe)一个通道,其他任何一个节点都可以作为发布者(Publisher)在该通道上发布(Publish)消息。
在Crawlab中,主节点会订阅nodes:master通道,其他节点如果需要向主节点发送消息,只需要向nodes:master
发布消息就可以了。同理,各工作节点会各自订阅一个属于自己的通道nodes:<node_id>
(node_id是MongoDB里的节点ID,是MongoDB ObjectId),如果需要给工作节点发送消息,只需要发布消息到该通道就可以了。
一个网络请求的简单过程如下:
客户端(前端应用)发送请求给主节点(API);
主节点通过Redis PubSub的<nodes:<node_id>
通道发布消息给相应的工作节点;
工作节点收到消息之后,执行一些操作,并将相应的消息通过<nodes:master>
通道发布给主节点;
主节点收到消息之后,将消息返回给客户端。
Crawlab的获取日志、获取系统信息、取消任务、告知节点获取爬虫文件都是通过即时通信完成的。
而实现代码相对来说有些复杂。下面是主节点的PubSub回调函数。
func MasterNodeCallback(channel string, msgStr string) { // 反序列化 var msg NodeMessage if err := json.Unmarshal([]byte(msgStr), &msg); err != nil { log.Errorf(err.Error()) debug.PrintStack() return } if msg.Type == constants.MsgTypeGetLog { // 获取日志 fmt.Println(msg) time.Sleep(10 * time.Millisecond) ch := TaskLogChanMap.ChanBlocked(msg.TaskId) ch <- msg.Log } else if msg.Type == constants.MsgTypeGetSystemInfo { // 获取系统信息 fmt.Println(msg) time.Sleep(10 * time.Millisecond) ch := SystemInfoChanMap.ChanBlocked(msg.NodeId) sysInfoBytes, _ := json.Marshal(&msg.SysInfo) ch <- string(sysInfoBytes) } }
这里其实是用msg.Type
来区分消息类别,如果要扩展的话需要写不少if/else
。工作节点的回调函数也需要写类似的逻辑。
这个可能跟HTTP请求和RPC通信相较来说麻烦一些。不过,这其实和WebSocket非常像(对WebSocket不了解的同学可以看看韦世东最近的文章《开发者必知必会的 WebSocket 协议》),都需要在客户端和服务端定义回调函数。一个改进方法是不用if/else
来区分信息类别,转而用PubSub频道名称,监听多个频道。总之,具体实践中怎么选择,还需要考虑实际情况。
延迟通信对即时性要求不高,不需要节点或客户端对请求即时回复。通常来说,延迟通信的实现方式有队列、轮询等方式。这样的方式不要求即时性。延迟通信主要是用作需要长时间的操作,例如发送邮件、数据处理、构建应用等等。
Crawlab中的延迟通信主要包含任务队列以及轮询,都是通过Redis来实现的。任务队列是用作爬虫任务执行:主节点接收抓取请求后,将执行任务的消息推到任务队列中,工作节点不断轮询任务队列,获取任务并执行(如下图)。Crawlab爬虫任务执行的详情请参见相关文档和源代码。
Crawlab的延迟通信主要包括爬虫任务执行和爬虫部署。爬虫任务执行这里不再赘述。下面简单介绍一下爬虫部署(流程如下图)。
整个爬虫部署的生命周期:
主节点每5秒,会从爬虫的目录获取爬虫信息,然后更新到数据库(这个过程不涉及文件上传);
主节点每60秒,从数据库获取所有的爬虫信息,然后将爬虫打包成zip文件,并上传到MongoDB GridFS,并且在MongoDB的spiders表里写入file_id文件ID;
主节点通过Redis PubSub发布消息(file.upload事件,包含文件ID)给工作节点,通知工作节点获取爬虫文件;
工作节点接收到获取爬虫文件的消息,从MongoDB GridFS获取zip文件,并解压储存在本地。
这样,所有爬虫将被周期性的部署在工作节点上。
在后续的开发中,Crawlab将会加入邮件通知、短信通知、微信推送等功能。而这些都是属于延迟通信的范畴,主要实现方法无外乎队列和轮询。
下面将介绍一个多机爬虫的实际应用场景,帮助大家深入理解Crawlab的分布式原理。
首先,你可能需要足够的网络带宽,因为需要抓取的网站上百了,不是简简单单的单机爬虫,你需要多台机器。这里只是简单介绍下拓扑架构,并不会详细介绍大规模爬虫的去重、反爬、容错等逻辑。如下图,每一个工作节点可以限制抓取一部分网站,总共M个网站平均分配给N个工作节点。在Crawlab中就是用指定节点的方式了,这个不难。另外,你也可以通过随机分配的方式来派发任务,每一个工作节点统计上也会均匀分配到任务,这其实也就是一个负载均衡(Load Balancing)的过程。
关于Crawlab的核心原理是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。