0%

项目介绍

网站外链爬虫,使用轻量级 Web 应用框架 Flask,以 Restful 接口提供服务,支持容器化部署。项目地址:https://github.com/s1mplecc/external-link-crawler ,需要 Python 版本 3.x。

对于传入的网站 URL,获取其 HTML 网页,提取外部链接的域名。包含如下几类外链:

  • 超链接,标签 a 下的 href 属性;
  • 图片,标签 img 下的 src 属性;
  • 外部样式文件,标签 linkhref 属性;
  • 外部 JavaScript 脚本文件,标签 scriptsrc 属性。

本地运行

Step 1:克隆项目。

1
$ git clone https://github.com/s1mplecc/external-link-crawler.git

Step 2:安装依赖,包括 Flask 和 BeautifulSoup4。建议使用 Virtualenv 局部安装依赖。

1
$ pip3 install -r requirements.txt 

Step 3:在 IDE 中运行或通过 Flask 命令行工具启动应用,端口号默认为 5000。生产环境可使用 Gunicorn 部署。

阅读全文 »

前言

前期工作:基于 Bind9 搭建内网 DNS 服务器,使得在 /etc/resolv.conf 中配置了该服务器 IP 的节点或终端的 DNS 流量都会流经该服务器,为集群 DNS 流量采集提供了先决基础。同时,初步使用日志记录 DNS 查询请求(配置在 /etc/named.conf 中),后续可采用 Packetbeat/TShark 等工具主动捕获。DNS 查询日志内容格式如下:

1
2
30-Nov-2021 08:57:25.192 client @0x7f74f800b650 192.168.101.145#61323 (apisix.apache.org): query: apisix.apache.org IN A + (168.168.168.47)
30-Nov-2021 08:57:25.192 client @0x7f7514148d20 192.168.101.145#61325 (github.com): query: github.com IN A + (168.168.168.47)

为了推进后续的 DNS 流量分类研究工作,本文介绍了如何基于现有的 DNS 流量日志,使用 Elasticsearch 和 Kibana 搭建流量监测可视化平台。本文包含以下内容:

  • 使用 Filebeat 将流量日志实时同步到 Elasticsearch;
  • 使用 Grok 正则过滤器对采集到的日志消息二次处理;
  • 使用网络数据包分析工具 Packetbeat

将日志文件转储到 Elasticsearch,一是作为历史归档考量,Bind9 日志存在存储时限,如果文件大小达到设置的上限,旧的日志文件会被循环覆盖。二是 JSON 格式易于拓展,支持嵌套和数组,且易于特征提取阶段 Python 进行处理。三是可视化支持好,在 Elasticsearch 数据集上,使用 Kibana 可以快速建立指标分析、图表分析以及时间序列分析等可视化。下图是基于 DNS 查询日志配置的 Kibana Dashboard 可视化界面。

93787acedd01f3ccf8ad7f2e4c62627d.png

实验环境

  • 操作系统 CentOS 7.9;
  • Docker,版本 19.03.13;
  • Elastic Stack 工具集,版本 7.15.2。包括 Elasticsearch、Kibana、Filebeat 和 Packetbeat。

部署 Elasticsearch + Kibana

阅读全文 »

MapReduce 架构

本文讨论的 MapReduce 架构是 Hadoop 1.0 版本时的架构,从 Hadoop 2.0 开始,Hadoop 推出了资源管理框架 YARN。在 YARN 中,使用 ResourceManager 来负责容器的调度(任务运行在容器中),以及作业的管理。使用 NodeManager 来向 ResourceManager 汇报节点资源以及容器运行状态,NodeManager 负责创建并管理执行任务的容器。从职责方面,ResourceManager 等同于 JobTracker,NodeManager 等同于 TaskTracker。大多数的分布式框架都符合这种主从设计,如 HDFS 的 NameNode 和 DataNode、Spark 的 Driver 和 Executor 等。

以下是 MapReduce 架构和工作流程中的常用术语:

  • Job(作业):是客户端需要执行的一个工作单元,包括输入数据、MapReduce 程序和配置信息;
  • Task(任务):Hadoop 将作业分成若干个任务来执行,任务分为两类,即 Map 任务和 Reduce 任务;
  • Map/Reduce:从执行阶段来看,Map 和 Reduce 代表两个大类阶段。从计算模型角度看,它们代表两个计算步骤。从代码层面看,它们是定义在 Mapper 和 Reducer 类中的函数;
  • Mapper/Reducer:从执行阶段的详细划分来看,Mapper 和 Reducer 代表执行 map 与 reduce 函数的步骤。从代码层面看,这是定义在代码中的两个 Java 类。某些语境下,Mapper 可以指代 Map 任务,Reducer 同理。

与多数的大数据分布式框架相同,MapReduce 的架构也遵循主从结构:

  • 运行在 HDFS NameNode 主节点上的 JobTracker 程序,负责接收从客户端提交的 Job,将其划分成 Map 任务和 Reduce 任务,分发给从节点 TaskTracker 执行。JobTracker 负责任务之间的协作,并通过 TaskTracker 发送来的心跳包维护集群的运行状态,以及作业进度信息。
  • 多个运行在 HDFS DataNode 节点上的 TaskTracker 程序,负责执行 Map 任务和 Reduce 任务,直接与 HDFS 交互。每隔一段时间,TaskTracker 向 JobTracker 发送心跳包,汇报节点运行状态,以及任务完成进度。

66b01a299a89595ca02de7b5b9f02b85.png

为了减少网络传输带来的性能影响,JobTracker 在分发 Map 任务时基于数据本地化优化(Data locality optimization)策略,将 Map 任务分发给包含此 Map 处理数据的从节点,并将程序 Jar 包发送给该 TaskTracker,遵循“运算移动,数据不移动”的原则。

新旧版本 API 说明

本文中的源码是 Hadoop 3.2.0 版本的源码,使用的是 Hadoop 新版 API。新版 API 位于 org.apache.hadoop.mapreduce 包下,旧版 API 位于 org.apache.hadoop.mapred 包下,两版 API 并不兼容,你可以在 这里 查看两者的区别。为保证向后兼容,Hadoop 并没有移除旧版 API,因此依赖库中两个版本并存,使用时要注意。

阅读全文 »

前言

为了免去繁杂的环境配置工作,提供开箱即用的 Spark + Hadoop 快捷部署方案。本教程基于 BitNami 项目的成熟镜像方案,搭建 Spark Docker 集群,并在原有镜像基础上,构建了安装有对应版本 Hadoop 的镜像。

镜像已提交至 Docker Hub 官方仓库中,可通过如下命令拉取:

1
docker pull s1mplecc/spark-hadoop:3

构建镜像的所需文件也已提交至 GitHub:s1mplecc/spark-hadoop-docker

实验环境

  • 操作系统 MacOS Mojave,命令行工具:Terminal + Zsh
  • Docker Desktop for Mac,内置 Docker CLI client 与 Docker Compose
  • Spark Docker 镜像:bitnami-docker-spark,Spark 版本:3.1.2
  • Hadoop 版本:hadoop-3.2.0

部署 Spark 集群

拉取镜像

BitNami 是一个开源项目,现已被 VMware 公司收购,其宗旨是简化在个人终端、Kubernetes 和云服务器等环境上的开源软件的部署。其已为 Docker Hub 社区提供了数百个容器镜像方案,其中的 Redis、MongoDB 等热门镜像更是超过了十亿次下载。

阅读全文 »

封装

面向对象程序设计(Object-oriented programming,缩写 OOP)是指一种程序设计范型,它强调一切行为都是基于对象(object)完成的,而对象则指的是(class)的实例。对象被作为程序的基本单元,数据和行为方法封装在其中,以提高软件的重用性、灵活性和扩展性,对象的行为方法可以访问和修改对象的数据。通过对象之间的相互协作,完成复杂的程序功能。面向对象编程语言具备封装、抽象、继承、多态等特性。

封装,又称信息隐藏,是指利用抽象数据类型(ADT)将数据和基于数据的操作封装在一起,尽可能地隐藏内部细节,只暴露一些公共接口与外部发生交互。面向对象编程语言使用类进行封装,数据和基于数据的操作对应于类的属性和方法。

具备封装性的面向对象程序设计隐藏了方法的具体执行步骤,取而代之的是对象之间的消息传递。举个例子,假设一个“歌唱家”想要“唱歌”,她当然知道自己该如何发声,但其他人没有必要了解她发声的细节,只管欣赏她美妙的歌喉。

1
2
3
4
5
6
7
8
9
10
/* 一个面向过程的程序会这样写: */
定义莱丝
莱丝.设置音调(5)
莱丝.吸气()
莱丝.吐气()

/* 当唱歌方法被封装到类中,任何歌唱家都可以简单地使用: */
定义歌唱家类
声明莱丝是一个歌唱家
莱丝.唱歌()

访问限制

使用封装能够对成员属性和方法进行精确的访问控制,通常来说,成员会依照它们的访问权限被分为3种:公有成员、私有成员以及保护成员,保护成员是指可以被子类访问的成员。有的语言更进一步:Java 专门提供了 public、private、protected 和缺省四个级别的访问权限控制关键字。Python 则更提倡开放,尽管没有强制要求,但也建议程序员使用带有下划线的命名风格来规范属性和方法的访问权限。

在 Python 中,非下划线开头的属性称为公有属性,单下划线或双下划线开头的属性称为私有属性,双下划线开头的私有属性不会被子类可见,Python 社区很少提及受保护的属性。PEP 8 提倡对于非公有方法和属性使用单个下划线开头,只有在避免子类命名冲突时才采用双下划线开头,这是因为解释器会改写双下划线开头的属性,改写为类名 + 变量名的格式。比如下面代码中的 __v3 就被改写为 _C__v3

1
2
3
4
5
6
7
>>> class C:
... v1 = 1
... _v2 = 2
... __v3 = 3
...
>>> [_ for _ in dir(C) if 'v' in _]
['_C__v3', '_v2', 'v1']

即便如此,Python 也不能严格保证私有属性不能被外部访问。子类之所以不能访问父类的双下划线开头的属性,只是因为改写后的属性名称不相符而已。

阅读全文 »