20、使用 Docker 创建爬虫微服务

使用 Docker 创建爬虫微服务

在当今的软件开发领域,微服务架构因其灵活性和可扩展性而备受青睐。结合 Docker 容器技术,我们可以更高效地部署和管理微服务。本文将详细介绍如何使用 Docker 创建爬虫微服务,包括创建爬虫微服务、构建容器以及创建 API 容器等步骤。

1. 远程过程调用(RPC)基础

远程过程调用(RPC)是一种在分布式系统中常用的通信机制。在 Nameko 框架中,RPC 调用会阻塞,直到从其他系统收到结果。例如,Nameko 调用 hello_microservice hello 方法时,会传递指定的字符串,并等待回复。与发布模型不同,发布模型发送消息后,发送应用程序会继续执行,而 RPC 会等待结果。

Nameko 还有一个很有用的特性,它可以为微服务的多个实例运行监听器,默认数量为 10 个。Nameko 会将微服务客户端的请求发送到 RabbitMQ 队列,有 10 个并发请求处理器监听该队列。如果请求过多,RabbitMQ 会保存消息,直到 Nameko 回收一个现有的微服务实例来处理排队的消息。为了提高微服务的可扩展性,我们可以通过配置增加工作线程的数量,或者在另一个 Docker 容器或计算机系统中运行单独的 Nameko 微服务容器。

2. 创建爬虫微服务

接下来,我们将把爬虫转换为 Nameko 微服务,这样爬虫就可以独立于 API 的实现运行、维护和扩展。具体步骤如下:

  1. 编写微服务代码 :代码位于 10/02/call_scraper_microservice.py ,示例代码如下:
from nameko.rpc import rpc
import sojobs.scraping

class ScrapeStackOverflowJobListingsMicroService:
    name = "stack_overflow_job_listings_scraping_microservice"
    @rpc
    def get_job_listing_info(self, job_listing_id):
        listing = sojobs.scraping.get_job_listing_info(job_listing_id)
        print(listing)
        return listing

if __name__ == "__main__":
    print(ScrapeStackOverflowJobListingsMicroService("122517"))
  1. 运行微服务 :在终端中使用 Nameko 运行服务:
$ nameko run scraper_microservice
starting services:
stack_overflow_job_listings_scraping_microservice
Connected to amqp://guest:**@127.0.0.1:5672//
  1. 调用微服务 :使用 10/02/call_scraper_microservice.py 脚本调用微服务,代码如下:
from nameko.standalone.rpc import ClusterRpcProxy
CONFIG = {'AMQP_URI': "amqp://guest:guest@localhost"}
with ClusterRpcProxy(CONFIG) as rpc:
    result = rpc.stack_overflow_job_listings_scraping_microservice.get_job_listing_info("122517")
    print(result)

运行上述代码后,会输出类似以下的结果(已截断):

{"ID": "122517", "JSON": {"@context": "http://schema.org", "@type": "JobPosting", "title": "SpaceX Enterprise Software Engineer, Full Stack", "skills": ["c#", "sql", "javascript", "asp.net", "angularjs"]}

此时,我们已经成功创建了一个从 StackOverflow 获取职位列表的微服务。不过,这个微服务只能使用 ClusterRpcProxy 类调用,不能通过 REST 接口被互联网上的任何人甚至本地调用。后续我们会解决这个问题。

3. 创建爬虫容器

为了将爬虫微服务部署到容器中,我们需要创建一个 Docker 容器。以下是具体步骤:

3.1 准备工作

首先,确保 RabbitMQ 在容器中运行,并分配到自定义的 Docker 网络中。可以使用以下命令查看当前安装的网络:

$ docker network ls
NETWORK ID   NAME                                     DRIVER  SCOPE
bc3bed092eff bridge                                   bridge  local
26022f784cc1 docker_gwbridge                          bridge  local
448d8ce7f441 dockercompose2942991694582470787_default bridge  local
4e549ce87572 dockerelkxpack_elk                       bridge  local
ad399a431801 host                                     host    local
rbultxlnlhfb ingress                                  overlay swarm
389586bebcf2 none                                     null    local
806ff3ec2421 stackdockermaster_stack                  bridge  local

为了让容器之间能够通信,我们创建一个名为 scraper-net 的新桥接网络:

$ docker network create --driver bridge scraper-net
e4ea1c48395a60f44ec580c2bde7959641c4e1942cea5db7065189a1249cd4f1

然后启动 RabbitMQ 容器并将其连接到 scraper-net 网络:

$ docker run -d --name rabbitmq --network scraper-net -p 15672:15672 -p 5672:5672 rabbitmq:3-management
3.2 创建 Dockerfile

10/03 文件夹中有一个 Dockerfile,内容如下:

FROM python:3
WORKDIR /usr/src/app
RUN pip install nameko BeautifulSoup4 nltk lxml
RUN python -m nltk.downloader punkt -d /usr/share/nltk_data all
COPY 10/02/scraper_microservice.py .
COPY modules/sojobs sojobs
CMD ["nameko", "run", "--broker", "amqp://guest:guest@rabbitmq", "scraper_microservice"]
3.3 构建容器镜像

10/03 文件夹的终端中运行以下命令构建容器镜像:

$ docker build ../.. -f Dockerfile -t scraping-microservice

构建过程可能需要一些时间,因为需要下载所有的 NLTK 文件到容器中。构建完成后,可以使用以下命令检查镜像是否创建成功:

$ docker images | head -n 2
REPOSITORY            TAG    IMAGE ID     CREATED     SIZE
scraping-microservice latest 0e1409911ac9 3 hours ago 4.16GB
3.4 运行容器

使用以下命令运行构建好的镜像:

$ docker run --network scraper-net scraping-microservice
starting services:
stack_overflow_job_listings_scraping_microservice
Connected to amqp://guest:**@rabbitmq:5672//
3.5 测试容器

在另一个终端窗口中运行 Nameko shell:

$ nameko shell
Nameko Python 3.6.1 |Anaconda custom (x86_64)| (default, Mar 22 2017, 19:25:17)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] shell on darwin
Broker: pyamqp://guest:guest@localhost
In [1]:

在提示符中输入以下命令调用微服务:

n.rpc.stack_overflow_job_listings_scraping_microservice.get_job_listing_info("122517")

运行后会输出大量的爬取结果(已截断):

Out[1]: '{"ID": "122517", "JSON": {"@context": "http://schema.org", "@type": "JobPosting", "title": "SpaceX Enterprise Software Engineer, Full Stack", "skills": ["c#", "sql", "javascript", "asp.net"]}
4. Dockerfile 工作原理

接下来分析 Dockerfile 是如何构建 Docker 镜像的:
1. FROM python:3 :告诉 Docker 基于 Docker Hub 上的 Python 3 镜像构建容器镜像,这是一个预构建的安装了 Python 3 的 Linux 镜像。
2. WORKDIR /usr/src/app :指定所有文件操作的相对目录为 /usr/src/app
3. RUN pip install nameko BeautifulSoup4 nltk lxml :运行 pip 安装爬虫所需的各种库。
4. RUN python -m nltk.downloader punkt -d /usr/share/nltk_data all :安装 NLTK 数据文件。
5. COPY 10/02/scraper_microservice.py . :将 scraper_microservice.py 文件复制到容器镜像中。
6. COPY modules/sojobs sojobs :复制 sojobs 模块到容器中。
7. CMD ["nameko", "run", "--broker", "amqp://guest:guest@rabbitmq", "scraper_microservice"] :指定容器启动时要运行的命令,即使用 Nameko 运行 scraper_microservice.py 中的微服务,并连接到名为 rabbitmq 的 RabbitMQ 消息代理。

由于我们将爬虫容器和 RabbitMQ 容器都连接到了 scraper-net 网络,Docker 会自动将它们连接起来。Nameko shell 在 Docker 主机系统上运行,启动时会报告与 pyamqp://guest:guest@localhost 的 AMQP 服务器(RabbitMQ)通信。当我们在 shell 中执行命令时,Nameko shell 会将消息发送到本地主机。而 RabbitMQ 容器同时连接到了 scraper-net 网络和主机网络,只要我们在启动时映射了 5672 端口,就可以与 RabbitMQ 代理通信。另一个容器中的微服务会监听 RabbitMQ 容器中的消息,然后响应该容器,最后 Nameko shell 会获取到响应。

5. 创建 API 容器

目前,我们只能使用 AMQP、Nameko shell 或 Nameko ClusterRPCProxy 类与微服务进行通信。接下来,我们将把 Flask-RESTful API 放入另一个容器中,与其他容器一起运行,并进行 REST 调用。由于 API 代码还需要与 Elasticsearch 通信,因此我们还需要运行一个 Elasticsearch 容器。

5.1 启动 Elasticsearch 容器

使用以下命令在 scraper-net 网络中启动 Elasticsearch 容器:

$ docker run -e ELASTIC_PASSWORD=MagicWord --name=elastic --network scraper-net -p 9200:9200 -p 9300:9300 docker.elastic.co/elasticsearch/elasticsearch:6.1.1
5.2 编写 API 代码

10/04 文件夹中有一个 api.py 文件,实现了一个修改后的 Flask-RESTful API,代码如下:

from flask import Flask
from flask_restful import Resource, Api
from elasticsearch import Elasticsearch
from nameko.standalone.rpc import ClusterRpcProxy

app = Flask(__name__)
api = Api(app)
CONFIG = {'AMQP_URI': "amqp://guest:guest@rabbitmq"}

class JobListing(Resource):
    def get(self, job_listing_id):
        print("Request for job listing with id: " + job_listing_id)
        es = Elasticsearch(hosts=["elastic"])
        if (es.exists(index='joblistings', doc_type='job-listing', id=job_listing_id)):
            print('Found the document in Elasticsearch')
            doc = es.get(index='joblistings', doc_type='job-listing', id=job_listing_id)
            return doc['_source']
        print('Not found in Elasticsearch, trying a scrape')
        with ClusterRpcProxy(CONFIG) as rpc:
            listing = rpc.stack_overflow_job_listings_scraping_microservice.get_job_listing_info(job_listing_id)
            print("Microservice returned with a result - storing in Elasticsearch")
            es.index(index='joblistings', doc_type='job-listing', id=job_listing_id, body=listing)
            return listing

api.add_resource(JobListing, '/', '/joblisting/<string:job_listing_id>')

if __name__ == '__main__':
    print("Starting the job listing API ...")
    app.run(host='0.0.0.0', port=8080, debug=True)

代码主要有以下几个修改点:
1. Elasticsearch 对象创建 :将 Elasticsearch 对象的创建指向 scraper-net 网络中名为 elastic 的主机:

es = Elasticsearch(hosts=["elastic"])
  1. 移除 sojobs 模块调用 :使用 Nameko ClusterRpcProxy 对象调用爬虫容器中的爬虫微服务:
with ClusterRpcProxy(CONFIG) as rpc:
    listing = rpc.stack_overflow_job_listings_scraping_microservice.get_job_listing_info(job_listing_id)
  1. Flask 应用启动修改 :将 Flask 应用绑定到所有网络接口,并将端口改为 8080:
app.run(host='0.0.0.0', port=8080, debug=True)
5.3 创建 API 容器的 Dockerfile

10/04 文件夹中有一个 Dockerfile,内容如下:

FROM python:3
WORKDIR /usr/src/app
RUN pip install Flask-RESTful Elasticsearch Nameko
COPY 10/04/api.py .
CMD ["python", "api.py"]
5.4 构建和运行 API 容器

使用以下命令构建 API 容器:

$ docker build ../.. -f Dockerfile -t scraper-rest-api

然后使用以下命令运行容器:

$ docker run -d -p 8080:8080 --network scraper-net scraper-rest-api
5.5 检查容器运行状态

使用以下命令检查所有容器是否正在运行:

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55e438b4afcd scraper-rest-api "python -u api.py" 46 seconds ago Up 45 seconds 0.0.0.0:8080->8080/tcp vibrant_sammet
bb8aac5b7518 docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 3 hours ago Up 3 hours 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp elastic
ac4f51c1abdc scraping-microservice "nameko run --brok..." 3 hours ago Up 3 hours thirsty_ritchie
18c2f01f58c7 rabbitmq:3-management "docker-entrypoint..." 3 hours ago Up 3 hours 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp rabbitmq
5.6 测试 API

在主机的终端中使用 curl 命令测试 REST 端点:

$ curl localhost:8080/joblisting/122517
"{\"ID\": \"122517\", \"JSON\": {\"@context\": \"http://schema.org\", \"@type\": \"JobPosting\", \"title\": \"SpaceX Enterprise Software Engineer, Full Stack\", \"skills\": ["c#", "sql", "javas"]}

通过以上步骤,我们成功地将 API 和功能容器化,并在容器中运行了 RabbitMQ 和 Elasticsearch。不过,这种容器化方式需要创建多个 Dockerfile、容器和网络,并独立运行它们。幸运的是,我们可以使用 docker-compose 简化这个过程。

6. 使用 docker-compose 本地组合和运行爬虫

docker-compose 是一个用于定义和运行多容器 Docker 应用程序的工具。使用 docker-compose ,我们可以使用 YAML 文件配置应用程序的服务,然后通过一个简单的配置文件和单个命令创建并启动所有服务。

6.1 准备工作

使用 docker-compose 之前,需要确保它已经安装。在 macOS 上,Docker 会自动安装 docker-compose ,在其他平台上可能需要手动安装,可以参考 官方文档 进行安装。同时,确保之前创建的所有容器都已停止运行,因为我们将创建新的容器。

通过以上步骤,我们完成了使用 Docker 创建爬虫微服务、构建容器、创建 API 容器以及使用 docker-compose 简化部署的过程。这种方式可以提高开发和部署的效率,同时增强系统的可扩展性和可维护性。

使用 Docker 创建爬虫微服务

6.2 创建 docker-compose.yml 文件

在项目根目录下创建一个 docker-compose.yml 文件,用于定义和配置所有的服务。以下是一个示例 docker-compose.yml 文件:

version: '3'
services:
  rabbitmq:
    image: rabbitmq:3-management
    container_name: rabbitmq
    ports:
      - "15672:15672"
      - "5672:5672"
    networks:
      - scraper-net

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.1.1
    container_name: elastic
    environment:
      - ELASTIC_PASSWORD=MagicWord
    ports:
      - "9200:9200"
      - "9300:9300"
    networks:
      - scraper-net

  scraping-microservice:
    build:
      context: .
      dockerfile: 10/03/Dockerfile
    container_name: scraping-microservice
    networks:
      - scraper-net

  scraper-rest-api:
    build:
      context: .
      dockerfile: 10/04/Dockerfile
    container_name: scraper-rest-api
    ports:
      - "8080:8080"
    networks:
      - scraper-net

networks:
  scraper-net:
    driver: bridge

这个 docker-compose.yml 文件定义了四个服务: rabbitmq elasticsearch scraping-microservice scraper-rest-api ,并将它们连接到 scraper-net 网络。

6.3 使用 docker-compose 启动服务

在终端中,进入 docker-compose.yml 文件所在的目录,然后运行以下命令启动所有服务:

$ docker-compose up -d

-d 参数表示在后台运行容器。运行该命令后, docker-compose 会根据 docker-compose.yml 文件中的配置创建并启动所有服务。

6.4 检查服务运行状态

使用以下命令检查所有服务是否正常运行:

$ docker-compose ps

输出结果应该类似于以下内容:
| Name | Command | State | Ports |
| — | — | — | — |
| rabbitmq | docker-entrypoint.sh rabbitmq-server | Up | 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp |
| elasticsearch | /usr/local/bin/docker-entrypoint.sh eswrapper | Up | 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp |
| scraping-microservice | nameko run –broker amqp://guest:guest@rabbitmq scraper_microservice | Up | |
| scraper-rest-api | python -u api.py | Up | 0.0.0.0:8080->8080/tcp |

6.5 测试服务

同样,在主机的终端中使用 curl 命令测试 REST 端点:

$ curl localhost:8080/joblisting/122517

如果一切正常,你应该会看到与之前测试相同的结果。

6.6 停止和清理服务

当你不再需要这些服务时,可以使用以下命令停止并删除所有容器:

$ docker-compose down

这个命令会停止并删除所有由 docker-compose 创建的容器、网络和卷。

总结与展望

7.1 总结

通过本文的介绍,我们详细了解了如何使用 Docker 创建爬虫微服务。具体步骤包括:
1. 创建爬虫微服务 :使用 Nameko 框架将爬虫转换为微服务,实现了从 StackOverflow 获取职位列表的功能。
2. 构建容器 :通过 Dockerfile 构建了爬虫微服务容器和 API 容器,使用自定义网络 scraper-net 实现容器之间的通信。
3. 创建 API 容器 :将 Flask-RESTful API 放入容器中,与 Elasticsearch 和爬虫微服务进行交互,实现了 REST 接口的调用。
4. 使用 docker-compose 简化部署 :使用 docker-compose 工具,通过一个 YAML 文件定义和配置所有服务,实现了多容器应用的快速部署和管理。

这种基于 Docker 和微服务架构的开发和部署方式,具有以下优点:
- 可扩展性 :可以轻松地增加或减少微服务的实例数量,以应对不同的负载需求。
- 可维护性 :每个微服务都可以独立开发、测试和部署,降低了系统的耦合度,提高了维护效率。
- 灵活性 :可以根据需要选择不同的技术栈和工具,构建适合业务需求的微服务。

7.2 展望

在未来的开发中,我们可以进一步优化和扩展这个爬虫微服务系统:
- 性能优化 :可以使用缓存技术、异步处理等方式提高系统的性能和响应速度。
- 安全加固 :加强容器和微服务的安全防护,例如使用 SSL/TLS 加密通信、身份验证和授权机制等。
- 自动化部署 :结合 CI/CD 工具,实现自动化的构建、测试和部署流程,提高开发效率和质量。

以下是整个流程的 mermaid 流程图:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px

    A(远程过程调用基础):::process --> B(创建爬虫微服务):::process
    B --> C(创建爬虫容器):::process
    C --> D(Dockerfile 工作原理):::process
    D --> E(创建 API 容器):::process
    E --> F(使用 docker-compose 本地组合和运行爬虫):::process
    F --> G(总结与展望):::process

通过不断地学习和实践,我们可以更好地掌握 Docker 和微服务架构,构建出更加高效、稳定和安全的应用系统。希望本文能对你有所帮助,让你在使用 Docker 创建爬虫微服务的道路上迈出坚实的一步。

【直流微电网】径向直流微电网的状态空间建模与线性化:一种耦合DC-DC变换器状态空间平均模型的方法 (Matlab代码实现)内容概要:本文介绍了径向直流微电网的状态空间建模与线性化方法,重点提出了一种基于耦合DC-DC变换器状态空间平均模型的建模策略。该方法通过对系统中多个相互耦合的DC-DC变换器进行统一建模,构建出整个微电网的集中状态空间模型,并在此基础上实施线性化处理,便于后续的小信号分析与稳定性研究。文中详细阐述了建模过程中的关键步骤,包括电路拓扑分析、状态变量选取、平均化处理以及雅可比矩阵的推导,最终通过Matlab代码实现模型仿真验证,展示了该方法在动态响应分析和控制器设计中的有效性。; 适合人群:具备电力电子、自动控制理论基础,熟悉Matlab/Simulink仿真工具,从事微电网、新能源系统建模与控制研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握直流微电网中多变换器系统的统一建模方法;②理解状态空间平均法在非线性电力电子系统中的应用;③实现系统线性化并用于稳定性分析与控制器设计;④通过Matlab代码复现和扩展模型,服务于科研仿真与教学实践。; 阅读建议:建议读者结合Matlab代码逐步理解建模流程,重点关注状态变量的选择与平均化处理的数学推导,同时可尝试修改系统参数或拓扑结构以加深对模型通用性和适应性的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值