抢占式调度协程API服务器

码哥比特课程 2024-04-02 00:09:20

这篇文章的标题有些拗口,笔者简单解释一下。

本文是一款介绍开源API服务器Medge的文章。Medge是一个HTTP服务器,而其作者目前将其定位为API服务器方向。

Medge Github: https://github.com/Water-Melon/Medge

在Medge中,我们可以通过脚本语言Melang来编写和实现API逻辑。Melang是一种抢占式调度的协程语言,其每一个脚本任务都被视为一个协程运行在同一个单一线程内部,依据执行步长轮询调度。因此,在上层逻辑开发过程中,开发者无需考虑协程调度问题,也就无需考虑何时该调用某些特定函数让出CPU执行权。

下面我们进入正题,看一个完整的简单使用示例。

使用示例

当我们安装好Medge后,我们可以在命令行中输入

$ medge

来启动HTTP服务器。

我们使用如下命令参数来启动Medge:

$ medge -p 8080 -d /opt/medge/ -w 1

这里,

-p 是用于指定该HTTP服务器监听的TCP端口号,若不设置默认为80。本例中HTTP监听0.0.0.0:8080。-d 是用于指定API服务的统一入口目录路径,本例中是/opt/medge。我们稍后会对这个目录下的结构进行说明。若不指定本参数项,则默认为/opt/medge。-w 是用于指定HTTP的工作进程数。若不指定默认为1。

此时,服务器已经启动了,但是我们还没有给出API服务的具体处理逻辑。那么下面我们就来增加API服务。

上面提到了Medge的-d参数指定了API服务的统一入口目录,下面我们就给出这一路径下的大致目录树结构:

|- /opt/medge/ |- service_1/ |- entry.m |- ... |- service_2/ |- entry.m |- ...

可以看到,在/opt/medge目录下,存在两个服务,即:service_1和service_2,且每个服务下都有一个同名脚本文件entry.m。entry.m是服务的入口文件,即当Medge收到HTTP请求,找到对应服务后,就会立刻执行该服务的entry.m脚本进行处理。

在我们的这个示例中,我们仅演示一个API服务,因此本例中的目录树结构为:

|- /opt/medge/ |- 127.0.0.1:8080/ |- entry.m |- index.m

这里,127.0.0.1:8080是一个目录,之所以起这样的名字,是因为Medge中的服务名是以HTTP请求的Host命名和区分的。因为本例不存在使用域名发起HTTP请求,所以这里的目录名称就是这样一个IP+端口的形式。

下面,我们给出entry.m和index.m的脚本内容:

//entry.m/* * Implement a simple controller. * There are three variable injected in this script task: * 1. Req. Its prototype is: * Req { * method; //string e.g. GET POST DELETE ... * version; //string e.g. HTTP/1.0 HTTP/1.1 * uri; //string e.g. /index/index * args; //an key-value array, e.g. ["key":"val", ...] * headers; //an key-value array, e.g. ["Content-Type":"application/json", ...] * body; //string * } * * 2. Resp. Its prototype is: * Resp { * version; //same as Req's version * code; //integer e.g. 200 * headers; //same as Req's headers * body; //same as Req's body * } * *. 3. Basedir. A string of the base directory path. (Not used in this example) */#include "@/index.m"str = Import('str');sys = Import('sys');uri = str.slice(Req.uri, '/');ctlr = str.capitalize(uri[0]);o = $ctlr;if (sys.has(o, uri[1]) != 'method') { Resp.code = 404;} else { o.__action__ = uri[1]; Resp.body = o.__action__(); Resp.headers['Content-Length'] = str.strlen(Resp.body);}//index.mJson = Import('json');Index { @index() { Resp.headers['Content-Type'] = 'application/json'; return Json.encode(['code': 200, 'msg': 'OK']); }}

可以看到,entry.m前面包含了一些注释信息,其中提到,每个请求都会向其对应服务的entry.m脚本中注入3个变量:

Req 包含了请求有关的信息,如:方法、版本、资源路径、参数、请求头和请求体。Resp 包含了响应有关的信息,如:版本(默认与请求版本一致)、响应状态码(默认为200)、响应头和响应体。Basedir 是一个字符串,内容为Medge -d参数指定的路径。

简单来说,本例中的entry.m实现了一个非常简单的MVC框架中的控制器,它根据资源路径找到对应的处理类和类方法,并调用它们来处理HTTP请求。

本例中,我们将使用如下命令发起HTTP请求:

$ curl -v http://127.0.0.1:8080/index/index

所以,entry中会先对资源路径/index/index进行切片,获取第一个index后将其首字母大写。随后使用这个修改后的字符串作为类名,查找当前脚本环境中是否存在一个名为Index的类。找到后,调用其index(即资源路径的第二个index)方法处理请求。

在执行curl命令后,我们将看到其输出大致为:

* Trying 127.0.0.1:8080...* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)> GET /index/index HTTP/1.1> Host: 127.0.0.1:8080> User-Agent: curl/7.81.0> Accept: */*> * Mark bundle as not supporting multiuse< HTTP/1.1 200 OK< Content-Length: 23< Content-Type: application/json< * Connection #0 to host 127.0.0.1 left intact{"code":200,"msg":"OK"}结束语

目前Medge还只是个实验性项目,其中还有很多功能有待实现,例如目前它只支持HTTP/1.0 HTTP/1.1的版本,且暂未支持https。除此之外,目前Melang脚本仅支持MySQL数据库,且还没有对应的http库函数或类供开发者使用。

感兴趣的小伙伴可以访问其Github仓库。

感谢阅读!

0 阅读:0

码哥比特课程

简介:感谢大家的关注