玩会吧微服务改造
1. 基于 redis 实现 RPC 开发框架
市面上没有基于 redis 实现的 Python 异步 RPC 框架。
干脆自己写了一个最简版的。
1.1 客户端
连接 redis 服务器:
1 | async def connect(self): |
将远程方法序列化:
1 | def make_request(name, *args, **kwargs): |
每次调用都应该有一个唯一 id
,用此 id
作为回复队列的 key
。
远程调用:
1 | async def call(self, name, *args, **kwargs): |
以 redis list 实现请求队列,将序列化后的请求字符串 push
到队尾:
1 | await self.redis.rpush(self.queue, json.dumps(req)) |
然后在回复队列上阻塞等待:
1 | _, elem = await self.redis.blpop(f'{self.queue}:{req_id}') |
1.2 服务端
run
起来之后,首先同客户端一样,连接到 redis 服务器。
在请求队列上阻塞等待并解包:
1 | while True: |
push
结果到回复队列:
1 | async def response(self, req_id, error, result): |
方法注册装饰器:
1 | def task(self, func): |
1.3 下载
pypi:https://pypi.org/project/asyncredisrpc/
源码:https://github.com/LiangZuoting/aioredisrpc
2. 项目结构改造
改造后的目录结构:
1 | . |
- common:通用能力模块,由其它模块导入使用
- microservices:rpc 服务
- webapi:web 网关,sanic 框架实现。通过 rpc 协议与几个 rpc 服务通信
- websocket:websocket 服务,同样通过 sanic 框架实现
3. 通过 Docker 部署
3.1 创建镜像
照惯例,Dockerfile
、requirements.txt
还有创建镜像的批处理脚本都放在了每个服务的根目录下。
但这样有一个问题,Dockerfile
不允许出现上层目录,即 ..
,也就是说没法把 common copy
到镜像。
需要在 docker build
时指定工作目录,以 webapi 为例:
1 | docker build -t liangzuoting/wanhuiba:webapi -f ./Dockerfile ../ |
最后的一个参数就是这个作用,把工作目录上移一层,即 common 所在目录。
同时,Dockerfile
中所有源路径都应该是相对于此工作目录的。比如:
1 | COPY ./webapi/requirements.txt /webapi/ |
3.2 添加 PYTHONPATH
因为 common 模块在服务模块的上层目录里,按 python 默认的模块搜索规则找不到。
需要将 common 在镜像中的路径添加到环境变量 PYTHONPATH
中:
1 | # Dockerfile 中设置环境变量 |
4. 已知问题
4.1 日志服务
没有找到一个轻量、免费、易用的日志服务。 sentry 偏重错误跟踪;graylog、logstash 又太重。
可能还是要用自己的 rpc 框架搞一个简易的日志服务。