You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7sDream 4a0bfcde4d
finish usage doc
6 months ago
chong finish usage doc 6 months ago
example finish usage doc 6 months ago
.gitignore finish usage doc 6 months ago
LICENSE add README and LICENSE 7 months ago
Pipfile init 7 months ago
Pipfile.lock init 7 months ago
README.md finish usage doc 6 months ago
run_example.py init 7 months ago
setup.py add README and LICENSE 7 months ago

README.md

Chong

chong.py 是一个实验(玩具)性质的 RPC Framework,想法是通过一个最简单的 RPC 框架的实现来说明 RPC 技术的一些基础原理。

依赖

除了 pipenv 本身之外,其他依赖包可通过 pipenv install 安装。

使用文档

使用 chong 开发 RPC 服务很简单,下面介绍通用的步骤。

安装

由于只是玩具项目,没有发布到 pypi,所以需要先竟然 pipenv 环境,执行安装:

git clone https://git.7sdre.am/7sDream/chong.py.git
cd chong
pipenv install
pipenv shell
python setup.py develop

然后执行 chong -h,如果有帮助文本出现,即说明安装完成。

定义接口

和其他 RPC 框架一样,第一步是定义接口。

chong 使用 yaml 文件格式定义接口,整个文件的根对象为 chong,其中含有二个子项:

  • struct: 数据结构定义
  • apis:接口定义

每个 struct 的定义由一系列字段定义组成,每个字段只需要设置字段名和类型即可。

每个 API 接口以接口名为名称,由参数类型,返回值类型两个子项组成。

之所以不允许多参数,是因为多个参数也可以用结构体来实现,而且这样更为统一,也更容易实现,同时也并没有损失任何能力。

下面是一个简单的 adder 的接口定义文件,供参考:

# example/protocol/adder.yml

chong:
  structs:
    Info:
      status: int
      message: str
    Argument:
      a: [int]
      b: [int]
    Result:
      info: Info
      sum: [int]
  apis:
    add:
      argument: Argument
      return: Result

(目前类型只支持 intstrlist,而且对 list,并不提供成员类型检查功能,所以嵌套的 list 可能会有问题,没什么时间改,先凑合着用一下)

这个定义文件中声明了三个类型,一个接口,含义用 C++ 的代码写出来大概是这样:

struct Info {
    int status;
    message std::string;
};

struct Argument {
    std::vector<int> a;
    std::vector<int> b;
};

struct Result {
    Info info;
    std::vector<int> sum;
};

class Adder {
    Result add(Argument args) {
        // ...
    }
}

yaml 语法简单,应该不需要进一步的说明吧。

代码生成

使用 chong CLI 工具可以生成客户端和服务端的样板代码:

cd example/protocol
chong adder.yml

此时会生成三个文件:

  • adder_strut.py:所有的数据结构定义
  • adder_client.py:客户端 Client 实现,客户端直接使用即可
  • adder_server.py:服务端 Server 的基类,服务端需要继承此类,实现接口

这三个文件都不需要打开看,这部分只讲使用,不讲原理。

服务端实现

# example/server.py

from example.protocol.adder_structs import Argument, Result
from .protocol.adder_server import AdderServer


class AdderImpl(AdderServer):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

    async def add(self, arg: Argument) -> Result:
        print(f"{arg.a} {arg.b}")
        return Result({
            "info": {
                "status": 0,
                "message": "OK"
            },
            "sum": [a + b for a, b in zip(arg.a, arg.b)]
        })


async def start() -> None:
    server = AdderImpl(7777)
    await server.start()

这里 AdderImpl 继承了 adder_server 里的 AdderServer 类,(除了构造函数之外)实现了我们在接口定义文件里定义的 add 接口。

模版代码中的服务端基类接受两个参数,第一个是端口(也即 start 函数中的 7777),第二个是 IP,IP 不填默认为所有。

这个 start 函数只是为了方便使用而已,不是必须。

客户端使用

CLI 产生的 AdderClient 类可以直接使用,不需要额外再写什么代码:

# example/client.py

import random
import asyncio

from .protocol.adder_structs import Argument
from .protocol.adder_client import AdderClient


async def start() -> None:
    adder = AdderClient("127.0.0.1", 7777)

    while True:
        a = [random.randint(0, 100) for _ in range(10)]
        b = [random.randint(0, 100) for _ in range(10)]
        arg = Argument({
            "a": a,
            "b": b,
        })

        result = await adder.add(arg)

        print(f"status: {result.info.status}, {result.info.message}")
        print(f"{arg.a} + {arg.b} = {result.sum}")

        await asyncio.sleep(1)

AdderClient 类也接受端口和 IP 两个参数 ,所以这里填写的端口需要和服务端一致。

运行结果

然后分别跑起来服务端和客户端即可,我这里写了一个 run_example.py 来辅助启动:

# run_example.py

import sys
import asyncio

import example.server
import example.client

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "server":
        asyncio.get_event_loop().run_until_complete(example.server.start())
        asyncio.get_event_loop().run_forever()
    else:
        asyncio.get_event_loop().run_until_complete(example.client.start())

python run_example.py server 启动服务端,python run_example.py 启动客户端,以下是结果:

example-result

LICENSE

MIT