DEV Community

Cover image for Preemptive scheduling coroutine API server
Megabit
Megabit

Posted on

Preemptive scheduling coroutine API server

This article introduces the open source API server Medge. Medge is an HTTP server, and it is positioned as an API server.

In Medge, we can use the scripting language Melang to write and implement API logic. Melang is a preemptive scheduling coroutine language, in which each script task is regarded as a coroutine running inside the same single thread, and is scheduled according to the execution step. Therefore, during the development of upper-level logic, developers do not need to consider the problem of coroutine scheduling, nor do they need to consider when to call certain functions to give up CPU execution rights.

Now let’s see a complete simple usage example.

Example

After we have installed Medge, we can use the command medge to start HTTP server.

In this example, we will execute the command as shown below.

$ medge -p 8080 -d /opt/medge/ -w 1
Enter fullscreen mode Exit fullscreen mode
  • -p is used to specify the TCP port number that the HTTP server listens to, if not set, the default is 80. In this example the HTTP server listens on 0.0.0.0:8080.
  • -d is used to specify the uniform entry directory path of the API services, in this case it is /opt/medge. We will explain the tree structure of this directory later. If this parameter item is not specified, it defaults to /opt/medge.
  • -w is used to specify the number of worker processes. Defaults to 1 if not specified.

At this point, the server has been started, but we have not given the specific processing logic of the API service. Then let’s add API services.

As mentioned above, the -d parameter of Medge specifies the unified entry directory of the API services. Below we give the approximate directory tree structure under this path:

|- /opt/medge/
    |- service_1/
        |- entry.m
        |- ...
    |- service_2/
        |- entry.m
        |- ...
Enter fullscreen mode Exit fullscreen mode

It can be seen that there are two services in the /opt/medge directory, namely: service_1 and service_2, and each service has a script file entry.m with the same name. entry.m is the entry file of the service, that is, when Medge receives an HTTP request and finds the corresponding service, it will immediately execute the entry.m script of the service for processing.

In our example, we only has one API service, so the directory tree in this example is:

|- /opt/medge/
    |- 127.0.0.1:8080/
        |- entry.m
        |- index.m
Enter fullscreen mode Exit fullscreen mode

Here, 127.0.0.1:8080 is a directory. The reason for this name is that the service name in Medge is named and distinguished by the header field Host of the HTTP request. And in this example, HTTP server does not have a domain name, so the directory name here is in the form of such an IP:port.

Below, we give the script content of entry.m and 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);
}
Enter fullscreen mode Exit fullscreen mode
//index.m

Json = Import('json');

Index {
    @index() {
        Resp.headers['Content-Type'] = 'application/json';
        return Json.encode(['code': 200, 'msg': 'OK']);
    }
}
Enter fullscreen mode Exit fullscreen mode

It can be seen that entry.m contains some comment information in front of it, which mentions that each request will inject 3 variables into the entry.m script of its corresponding service:

  • Req contains information about the request, such as: method, version, resource path, parameters, request headers and request body.
  • Resp contains information about the response, such as: version (default is the same as the request version), response status code (default is 200), response header and response body.
  • Basedir is a string containing the path specified by the Medge -d parameter.

entry.m in this example implements a very simple controller in the MVC framework, which finds the corresponding processing class and class method according to the resource path, and calls method to process the HTTP request.

In this example, we will use the following command to send an HTTP request:

$ curl -v http://127.0.0.1:8080/index/index
Enter fullscreen mode Exit fullscreen mode

Therefore, in entry.m, the resource path /index/index will be sliced at first, and the first letter of it will be capitalized after obtaining the first index. Then use this capitalized string as the class name to find if there is a class called Index in the current scripting environment. After finding it, call its index (that is, the second index of the resource path) method to process the request.

After executing the curl command, we will see that its output is:

*   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"}
Enter fullscreen mode Exit fullscreen mode

At the end

At present, Medge is still an experimental project, and there are still many functions to be implemented. For example, it only supports HTTP/1.0 HTTP/1.1 version at present, and does not support https yet. In addition, Melang scripts currently only support MySQL databases, and there are no corresponding http library functions or classes for developers to use.

For more details, please visit its Github repository.

Thanks for reading!

Top comments (0)