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
python
import os
MONGO_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
python
from motor.motor_tornado import MotorClient
from common.config import MONGO_URI, DB_NAME
client = MotorClient(MONGO_URI)
db = client[DB_NAME]
utils.py
python
import json
import tornado.web
class 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
Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY ../../requirements.txt ./
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "main.py"]
main.py
python
import tornado.ioloop
import tornado.web
from tornado.process import fork_processes
from handlers.user_handler import UserHandler
from common.config import PORT
def make_app():
return tornado.web.Application([
(r"/user", UserHandler),
])
if __name__ == '__main__':
fork_processes(None) # 多进程 worker
app = make_app()
app.listen(PORT)
tornado.ioloop.IOLoop.current().start()
handlers/user_handler.py
python
from common.utils import BaseHandler
from services.user_service import UserService
class 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
python
from common.db import db
import json
class 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
python
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
yaml
version: '3.8'
services:
mongo:
image: mongo:6.0
container_name: mongo
ports:
- "27017:27017"
api_service:
build: ./api_service
container_name: api_service
environment:
- MONGO_URI=mongodb://mongo:27017
- DB_NAME=microdb
- PORT=8000
ports:
- "8000:8000"
depends_on:
- mongo
user_service:
build: ./user_service
container_name: user_service
environment:
- MONGO_URI=mongodb://mongo:27017
- DB_NAME=microdb
- PORT=8001
ports:
- "8001:8001"
depends_on:
- mongo
七、Kubernetes 配置示例(k8s/)
api-deployment.yaml
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
replicas: 2
selector:
matchLabels:
app: api-service
template:
metadata:
labels:
app: api-service
spec:
containers:
- name: api-service
image: api_service:latest
ports:
- containerPort: 8000
api-service.yaml
yaml
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
type: ClusterIP
selector:
app: api-service
ports:
- port: 8000
targetPort: 8000
ingress.yaml
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: micro-ingress
spec:
rules:
- host: micro.local
http:
paths:
- path: /user
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8000
八、运行与测试
-
启动开发环境:
bashdocker-compose up --build
-
本地运行测试:
bashpytest -v
-
部署到 Kubernetes:
bashkubectl apply -f k8s/
九、说明
- Tornado 使用多进程 worker(
fork_processes(None)
)自动创建与 CPU 核数相同的进程。 - MongoDB 异步连接由 Motor 驱动支持。
- 测试通过
pytest-asyncio
实现异步单元测试。 - Kubernetes YAML 已含 Ingress,可直接对外暴露 API 服务。
这一结构可直接扩展为完整的微服务体系,每个服务都独立运行、可水平扩展。