Tornado + Motor 微服务架构(Docker + 测试 + Kubernetes)
Tornado + Motor 微服务架构(Docker + 测试 + Kubernetes)
一、项目结构
project-root/
├── docker-compose.yml
├── k8s/ # Kubernetes 配置文件
│ ├── api-deployment.yaml
│ ├── api-service.yaml
│ ├── user-deployment.yaml
│ ├── user-service.yaml
│ ├── mongo-deployment.yaml
│ ├── mongo-service.yaml
│ └── ingress.yaml
├── common/
│ ├── config.py
│ ├── db.py
│ ├── utils.py
├── api_service/
│ ├── Dockerfile
│ ├── main.py
│ ├── handlers/
│ │ └── user_handler.py
│ ├── services/
│ │ └── user_service.py
│ └── tests/
│ └── test_user.py
├── user_service/
│ ├── Dockerfile
│ ├── main.py
│ ├── handlers/
│ │ └── user_handler.py
│ ├── services/
│ │ └── user_service.py
│ └── tests/
│ └── test_user.py
└── requirements.txt
二、核心依赖(requirements.txt)
tornado==6.4
motor==3.3.2
pytest==8.2.0
pytest-asyncio==0.24.0
requests==2.32.3
三、公共模块(common/)
config.py
import osMONGO_URI = os.getenv('MONGO_URI', 'mongodb://mongo:27017')
DB_NAME = os.getenv('DB_NAME', 'microdb')
SERVICE_NAME = os.getenv('SERVICE_NAME', 'default-service')
PORT = int(os.getenv('PORT', 8000))
db.py
from motor.motor_tornado import MotorClient
from common.config import MONGO_URI, DB_NAMEclient = MotorClient(MONGO_URI)
db = client[DB_NAME]
utils.py
import json
import tornado.webclass BaseHandler(tornado.web.RequestHandler):def set_default_headers(self):self.set_header('Content-Type', 'application/json')def write_json(self, data, status=200):self.set_status(status)self.write(json.dumps(data))
四、API 服务(api_service/)
Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY ../../requirements.txt ./
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "main.py"]
main.py
import tornado.ioloop
import tornado.web
from tornado.process import fork_processes
from handlers.user_handler import UserHandler
from common.config import PORTdef make_app():return tornado.web.Application([(r"/user", UserHandler),])if __name__ == '__main__':fork_processes(None) # 多进程 workerapp = make_app()app.listen(PORT)tornado.ioloop.IOLoop.current().start()
handlers/user_handler.py
from common.utils import BaseHandler
from services.user_service import UserServiceclass UserHandler(BaseHandler):async def get(self):users = await UserService().get_all_users()self.write_json({"users": users})async def post(self):data = self.request.body.decode()result = await UserService().create_user(data)self.write_json(result)
services/user_service.py
from common.db import db
import jsonclass UserService:async def get_all_users(self):cursor = db.users.find()return [doc async for doc in cursor]async def create_user(self, data):user = json.loads(data)result = await db.users.insert_one(user)return {"_id": str(result.inserted_id)}
tests/test_user.py
import pytest
import asyncio
from services.user_service import UserService@pytest.mark.asyncio
async def test_create_user():service = UserService()res = await service.create_user('{"name":"test_user"}')assert "_id" in res
五、User 服务(user_service/)
结构相同,只是接口路径不同,例如 /profile
。
六、Docker Compose
docker-compose.yml
version: '3.8'
services:mongo:image: mongo:6.0container_name: mongoports:- "27017:27017"api_service:build: ./api_servicecontainer_name: api_serviceenvironment:- MONGO_URI=mongodb://mongo:27017- DB_NAME=microdb- PORT=8000ports:- "8000:8000"depends_on:- mongouser_service:build: ./user_servicecontainer_name: user_serviceenvironment:- MONGO_URI=mongodb://mongo:27017- DB_NAME=microdb- PORT=8001ports:- "8001:8001"depends_on:- mongo
七、Kubernetes 配置示例(k8s/)
api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: api-deployment
spec:replicas: 2selector:matchLabels:app: api-servicetemplate:metadata:labels:app: api-servicespec:containers:- name: api-serviceimage: api_service:latestports:- containerPort: 8000
api-service.yaml
apiVersion: v1
kind: Service
metadata:name: api-service
spec:type: ClusterIPselector:app: api-serviceports:- port: 8000targetPort: 8000
ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: micro-ingress
spec:rules:- host: micro.localhttp:paths:- path: /userpathType: Prefixbackend:service:name: api-serviceport:number: 8000
八、运行与测试
-
启动开发环境:
docker-compose up --build
-
本地运行测试:
pytest -v
-
部署到 Kubernetes:
kubectl apply -f k8s/
九、说明
- Tornado 使用多进程 worker(
fork_processes(None)
)自动创建与 CPU 核数相同的进程。 - MongoDB 异步连接由 Motor 驱动支持。
- 测试通过
pytest-asyncio
实现异步单元测试。 - Kubernetes YAML 已含 Ingress,可直接对外暴露 API 服务。
这一结构可直接扩展为完整的微服务体系,每个服务都独立运行、可水平扩展。