在现代软件开发中,微服务已成为一种举足轻重的架构,可实现复杂系统的可扩展性、灵活性和高效管理。
微服务是执行特定任务的小型独立应用程序,允许灵活部署和扩展。这种模块化的软件设计方法松散了组件之间的耦合,提高了整个开发过程的灵活性和可管理性。
本文概述了微服务、微服务的功能以及使用 Python 创建微服务的过程。文章还演示了使用 Dockerfile 将微服务部署到云服务器。
什么是微服务?
微服务是应用程序中独立自主的服务,每个服务都能满足特定的业务需求。它们通过轻量级应用程序接口或消息代理进行通信,形成一个综合系统。
与完全根据需求进行扩展的单体系统不同,微服务允许扩展单个高流量组件。这种架构便于故障管理和功能更新,消除了单体系统的局限性。
使用微服务有以下几个好处:
不过,微服务架构也面临一些挑战:
- 监控多个服务-由于特定服务的实例被部署并分布在多个节点上,因此监控系统中的单个服务变得非常困难。这种困难在网络故障或其他系统问题时尤为明显。
- 成本-由于管理多个服务的相关成本,开发微服务应用程序的成本可能比构建单体系统高得多。每个服务都需要自己的基础设施和资源,这可能会导致成本高昂–尤其是在系统扩展时。
如何使用 Python 设计微服务
既然您已经了解了使用微服务架构的好处,那么现在就该用 Python 构建一个微服务架构了。
在这个示例中,假设您想构建一个电子商务 Web 应用程序。该网站有几个组件,包括产品目录、订单列表、支付处理系统和日志,您需要将每个组件作为独立的服务来实现。此外,您还需要建立服务与服务之间的通信方法,以便在这些服务(如 HTTP)之间高效地传输数据。
让我们使用 Python 构建一个微服务来管理产品目录。该微服务将从指定来源获取产品数据,并以 JSON 格式返回数据。
前提条件
要学习本教程,请确保您具备以下条件:
- 熟悉 Flask
- 计算机上安装了 Python、Postman 客户端和 Docker Desktop
1. 创建您的项目
首先,为项目创建一个名为 flask-microservice 的文件夹,并将当前目录放入项目目录。
接下来,运行 python3 --version
确认 Python 是否已正确安装到电脑上。
运行下面的命令安装 virtualenv
,为 Flask 微服务创建一个隔离的开发环境:
pip3 install virtualenv
运行以下程序创建虚拟环境:
virtualenv venv
最后,根据计算机操作系统使用以下命令之一激活虚拟环境:
# Windows: .\venv\Scripts\activate # Unix or macOS: source venv/bin/activate
2. 设置 Flask 服务器
在根目录下创建 requirements.txt 文件,并添加这些依赖项。
flask requests
在终端上运行 pip3
命令安装依赖项。
pip install -r requirements.txt
接下来,在根目录下新建一个文件夹,命名为 services。在该文件夹中创建一个新文件 products.py,并添加下面的代码来设置 Flask 服务器。
import requests import os from flask import Flask, jsonify app = Flask(__name__) port = int(os.environ.get('PORT', 5000)) @app.route("/") def home(): return "Hello, this is a Flask Microservice" if __name__ == "__main__": app.run(debug=True, host="0.0.0.0", port=port)
在上面的代码中,设置了一个基本的 Flask 服务器。它初始化了一个 Flask 应用程序,为根 URL(” /
“)定义了一条路由,并在访问时显示 "Hello, this is a Flask Microservice"
的消息。服务器在指定的端口(从环境变量中获取或默认为端口 5000
)上运行,并以调试模式启动,以便随时处理传入的请求。
3. 定义 API 端点
配置好服务器后,为微服务创建一个 API 端点,以便从公开可用的 API 中获取产品数据。将此代码添加到 products.py 文件中:
BASE_URL = "https://dummyjson.com" @app.route('/products', methods=['GET']) def get_products(): response = requests.get(f"{BASE_URL}/products") if response.status_code != 200: return jsonify({'error': response.json()['message']}), response.status_code products = [] for product in response.json()['products']: product_data = { 'id': product['id'], 'title': product['title'], 'brand': product['brand'], 'price': product['price'], 'description': product['description'] } products.append(product_data) return jsonify({'data': products}), 200 if products else 204
上面的代码在 Flask 服务器中创建了一个 /products
端点。当通过 GET 请求访问时,它会从一个虚拟应用程序接口获取产品数据。如果成功,它会处理检索到的数据,提取产品详细信息,并以 JSON 格式返回信息。如果出现错误或没有可用数据,它会以适当的错误消息和状态代码进行响应。
4. 测试微服务
至此,您已经成功建立了一个简单的微服务。要启动服务,请启动开发服务器,它将在 http://localhost:5000
开始运行。
flask --app services/products run
然后使用 Postman 客户端向 /products
端点发出 GET
请求,你应该会看到与下面截图类似的响应。
在 Postman 中测试 HTTP API GET
请求。
如何在 Python 微服务中实施身份验证和授权
在构建微服务时,实施强大的安全措施(如身份验证和授权)非常重要。保证微服务的安全可以确保只有授权用户才能访问和使用服务,从而保护敏感数据并防止恶意攻击。
在微服务中实施安全身份验证和授权的一种有效方法是 JSON Web 标记(JWT)。
JWT 是一种广泛使用的开放标准,它提供了一种在客户端和服务器之间传输身份验证信息的安全高效的方法。它们是经过加密和数字签名的小巧令牌,可与 HTTP 请求一起传递。当你在每个请求中包含一个 JWT 时,服务器就能快速验证用户的身份和权限。
要在微服务中实施 JWT 身份验证,请执行以下操作:
在 requirements.txt 文件中添加 Python 的 pyjwt
软件包,并使用 pip install -r requirements.txt
重新安装依赖项。
由于该服务没有专用数据库,因此请在项目根目录下创建 users.json 文件,以存储授权用户列表。将下面的代码粘贴到文件中:
[ { "id": 1, "username": "admin", "password": "admin" } ]
注:您可以使用我们的数据库托管服务,为您的微服务轻松设置您喜欢的数据库(PostgreSQL、MariaDB、Redis 和 MySQL)。
然后,在您的 services/products.py 文件中,用以下语句替换导入语句:
import requests from flask import Flask, jsonify, request, make_response import jwt from functools import wraps import json import os from jwt.exceptions import DecodeError
导入这些模块是为了处理 HTTP 请求、创建 Flask 应用程序、管理 JSON 数据、实现基于 JWT 的身份验证和处理异常,从而在 Flask 服务器中启用各种功能。
在 Flask 应用程序实例创建下方添加以下代码,以生成用于签署 JWT 标记的秘钥。
app.config['SECRET_KEY'] = os.urandom(24)
要验证 JWT,请创建一个装饰器函数,并在 Flask 服务器代码中的 API 路由上方添加以下代码。此装饰器函数将在用户访问受保护路由之前对其进行身份验证和验证。
def token_required(f): @wraps(f) def decorated(*args, **kwargs): token = request.cookies.get('token') if not token: return jsonify({'error': 'Authorization token is missing'}), 401 try: data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"]) current_user_id = data['user_id'] except DecodeError: return jsonify({'error': 'Authorization token is invalid'}), 401 return f(current_user_id, *args, **kwargs) return decorated
此装饰器函数会检查传入 HTTP 请求中的 JWT 授权令牌,该令牌应存在于请求头或 cookie 中。如果令牌缺失或无效,装饰器将 unauthorized status code
发送未授权状态代码消息作为响应。
相反,如果存在有效令牌,装饰器会在解码后提取用户 ID。这一过程只允许授权用户访问受保护的应用程序接口端点,从而保障了应用程序接口端点的安全。
使用下面的代码定义一个用于用户身份验证的 API 端点。
with open('users.json', 'r') as f: users = json.load(f) @app.route('/auth', methods=['POST']) def authenticate_user(): if request.headers['Content-Type'] != 'application/json': return jsonify({'error': 'Unsupported Media Type'}), 415 username = request.json.get('username') password = request.json.get('password') for user in users: if user['username'] == username and user['password'] == password: token = jwt.encode({'user_id': user['id']}, app.config['SECRET_KEY'],algorithm="HS256") response = make_response(jsonify({'message': 'Authentication successful'})) response.set_cookie('token', token) return response, 200 return jsonify({'error': 'Invalid username or password'}), 401
为了验证和授权用户, /auth
API 端点会根据允许的用户列表检查 POST
请求的 JSON 有效负载中的凭据。如果凭据有效,它就会使用用户 ID 和应用程序秘钥生成一个 JWT 令牌,并在响应中将令牌设置为 cookie。现在,用户可以使用此令牌进行后续的 API 请求。
创建 /auth
端点后,使用 Postman 向 http://localhost:5000/auth
发送 HTTP POST
请求。在请求正文中,包含你创建的模拟管理员用户的凭据。
Postman 请求显示请求正文。
如果请求成功,API 将生成一个 JWT 令牌,将其设置在 Postman 的 cookies 中,并发送验证成功的响应。
最后,更新 GET
API 端点,使用下面的代码检查并验证 JWT 标记:
@app.route('/products', methods=['GET']) @token_required def get_products(current_user_id): headers = {'Authorization': f'Bearer {request.cookies.get("token")}'} response = requests.get(f"{BASE_URL}/products", headers=headers) if response.status_code != 200: return jsonify({'error': response.json()['message']}), response.status_code products = [] for product in response.json()['products']: product_data = { 'id': product['id'], 'title': product['title'], 'brand': product['brand'], 'price': product['price'], 'description': product['description'] } products.append(product_data) return jsonify({'data': products}), 200 if products else 204
如何使用 Docker 将 Python 微服务容器化
Docker 是一个将应用程序及其依赖关系打包到一个隔离的开发环境中的平台。将微服务打包到容器中可简化其在服务器中的部署和管理流程,因为每个服务都在其容器中独立运行和执行。
要将微服务容器化,必须从指定在容器中运行应用程序所需的依赖项的 Docker 文件创建 Docker 映像。在项目根目录中创建一个 Dockerfile,并添加以下说明:
FROM python:3.9-alpine WORKDIR /app COPY requirements.txt ./ RUN pip install -r requirements.txt COPY . . EXPOSE 5000 CMD ["python", "./services/products.py"]
在构建镜像之前,请查看这些命令:
FROM
– 指示 Docker 使用哪个基础镜像。基础镜像是预先构建的实例,包含在容器中运行 Flask 应用程序所需的软件和依赖项。WORKDIR
– 将容器中的指定目录设置为工作目录。COPY requirements.txt ./
– 将 requirements.txt 文件中的依赖项复制到容器的 requirements.txt 文件中。RUN
– 运行指定命令以安装映像所需的依赖项。COPY . .
– 将项目根目录中的所有文件复制到容器内的工作目录。EXPOSE
– 指定容器监听请求的端口。不过,Docker 不会向主机发布该端口。CMD
– 指定容器启动时要执行的默认命令。
接下来,在项目根目录中添加 .dockerignore 文件,指定 Docker 镜像应排除的文件。限制映像内容将减少其最终大小和相关的构建时间。
/venv /services/__pycache__/ .gitignore
现在,运行下面的命令来构建 Docker 镜像:
docker build -t flask-microservice .
最后,镜像构建完成后,就可以使用以下命令在 Docker 容器中运行微服务了:
docker run -p 5000:5000 flask-microservice
该命令将启动一个运行微服务的 Docker 容器,并将容器上的 5000 端口暴露给主机上的 5000 端口,这样就可以使用 URL http://localhost:5000
从网络浏览器或 Postman 发出 HTTP 请求。
使用 Kinsta 部署 Python 微服务
Kinsta 为网络应用程序和数据库提供托管解决方案–您可以在生产环境中无缝部署和管理您的 Python 微服务和后端 API。
请按照以下步骤配置您的 Flask 微服务,以便使用 MyKinsta 进行部署:
首先,在根目录中创建一个新的 Procfile,并添加以下代码。它指定了在 Kinsta 的 Gunicorn WSGI HTTP 服务器(适用于 Python 应用程序)上运行 Flask 微服务的命令。
web: gunicorn services.wsgi
在 requirements.txt 文件中,添加 Gunicorn 依赖项:
gunicorn==20.1.*
接下来,创建一个新的 services/wsgi.py 文件并添加以下代码。
from services.products import app as application if __name__ == "__main__": application.run()
在项目根文件夹中创建 .gitignore 文件,并添加以下内容:
services/__pycache__ venv
最后,在 GitHub 上创建一个新仓库并推送项目文件。
仓库准备就绪后,按照以下步骤将 Flask 微服务部署到 Kinsta:
- 登录或创建账户,查看 MyKinsta 面板。
- 使用 Git 提供商(Bitbucket、GitHub 或 GitLab)授权 Kinsta。
- 单击左侧边栏上的 “Applications“,然后单击 “Add application“。
- 在仪表板上点击 Add Service,然后选择 Application。
- 选择要部署的版本库和分支。
- 为应用程序指定一个唯一的名称,并选择一个 data center location。
- 要配置 Build 环境,请选择使用 Dockerfile 构建容器映像的选项。
- 提供 Dockerfile 的路径和上下文。
- 最后,查看其他信息并单击 “Create application“。
测试微服务
部署过程成功后,点击提供的 URL,在 Postman 中发出 HTTP 请求,测试微服务。向根端点发出 GET
请求。
对微服务 product
端点的 HTTP API GET
请求。
要进行身份验证并生成 JWT 令牌,请向 /auth
API 端点发送 POST
请求,并在请求正文中传递管理员凭据。
HTTP API POST
请求到微服务 auth
端点。
最后,成功通过身份验证后,向 /products
端点发出 GET
请求以获取数据。
对微服务 products
端点的 HTTP API GET
请求。
小结
随着应用程序的规模和复杂性不断增加,采用能让软件系统在不占用可用资源的情况下进行扩展的架构模式至关重要。
微服务架构具有可扩展性、开发灵活性和可维护性,使您能更轻松地管理复杂的应用程序。
原文地址:https://www.wbolt.com/python-microservices.html