JWSGI接口

注解

JWSGI并不是一个标准,然而,如果你喜欢JWSGI,那么何不发送一个RFC给uWSGI邮件列表。我们对标准没有特别的兴趣,但是谁知道呢……

JWSGI是考虑Java的WSGI/PSGI/Rack方式的一个口子。

如果,由于某种模糊的原因,你想要用JVM语言开发应用,并且你不想要部署一个巨大的servlet栈,那么JWSGI应该与你同行。

它是一个非常简单的协议:调用一个公共方法,它接收一个 HashMap 作为它唯一的参数。这个HashMap包含了CGI风格的变量,而 jwsgi.input 包含了一个Java InputStream对象。

这个函数必须返回一个包含3个对象的数字:

  • status (java.lang.Integer) (例如:200)
  • headers (HashMap) (例如:{“Content-type”: “text/html”, “Server”: “uWSGI”, “Foo”: [“one”,”two”]})
  • body (可能是一个String,一个String数组,一个File或者一个InputStream对象)

例子

一个简单的JWSGI应用看起来像这样:

import java.util.*;
public class MyApp {

    public static Object[] application(HashMap env) {

        int status = 200;

        HashMap<String,Object> headers = new HashMap<String,Object>();
        headers.put("Content-type", "text/html");
        // a response header can have multiple values
        String[] servers = {"uWSGI", "Unbit"};
        headers.put("Server", servers);

        String body = "<h1>Hello World</h1>" + env.get("REQUEST_URI");

        Object[] response = { status, headers, body };

        return response;
    }
}

如何使用它?

你同时需要’jvm’插件和’jwsgi’插件。项目中有一个名为’jwsgi’的构建配置文件可以用,允许你实现带jvm+jwsgi的单片构建:

UWSGI_PROFILE=jwsgi make
  1. javac 编译你的类。

    javac MyApp.java
    
  1. 运行uWSGI,并且指定要运行的方法 (使用格式class:method)

    ./uwsgi --socket /tmp/uwsgi.socket --plugins jvm,jwsgi --jwsgi
    MyApp:application --threads 40
    
这将会在UNIX socket /tmp/uwsgi.socket上运行一个JWSGI应用,使用40个线程。

读取请求体

jwsgi.input 项是一个 uwsgi.RequestBody 对象 (java/io/InputStream的子类). 通过它来访问请求体。

import java.util.*;
public class MyApp {

    public static Object[] application(HashMap env) {

        int status = 200;

        HashMap<String,Object> headers = new HashMap<String,Object>();
        headers.put("Content-type", "text/plain");

        int body_len = Integer.parseInt((String) env.get("CONTENT_LENGTH"));
        byte[] chunk = new byte[body_len];

        uwsgi.RequestBody input = (uwsgi.RequestBody) env.get("jwsgi.input");

        int len = input.read(chunk);

        System.out.println("read " + len + " bytes");

        String body = new String(chunk, 0, len);

        Object[] response = { status, headers, body };

        return response;
    }
}

注意用 read(byte[]) 来代替传统的 read() 。后者低效地每次读取一个字节,而前者一次读取一个更大的块。

JWSGI和Groovy

因为它是低级别的,因此,JWSGI标准可以原样用于JVM上运行的其他语言。例如,这是一个”Hello World” Groovy样例:

static def Object[] application(java.util.HashMap env) {
     def headers = ["Content-Type":"text/html", "Server":"uWSGI"]
     return [200, headers, "<h1>Hello World</h1"]
}

一个提供静态文件服务的例子:

static def Object[] application(java.util.HashMap env) {
     def headers = ["Content-Type":"text/plain", "Server":"uWSGI"]
     return [200, headers, new File("/etc/services")]
}

第二个方法非常有效,因为它将滥用uWSGI内部功能。例如,如果你启用了卸载,那么你的worker线程将会被突然释放。要加载Groovy代码,记得编译它:

groovyc Foobar.groovy

然后运行它:

./uwsgi --socket /tmp/uwsgi.socket --plugins jvm,jwsgi --jwsgi Foobar:application --threads 40

JWSGI和Scala

就像Groovy,你可以用Scala编写JWSGI应用。你只需要入口点函数来使用原生的Java对象:

object HelloWorld {
     def application(env:java.util.HashMap[String, Object]): Array[Object] = {
             var headers = new java.util.HashMap[String, Object]()
             headers.put("Content-Type", "text/html")
             headers.put("Server", "uWSGI")
             return Array(200:java.lang.Integer, headers , "Hello World")
     }
}

或者一种更Scala的方式:

object HelloWorld {
     def application(env:java.util.HashMap[String, Object]): Array[Object] = {
             val headers = new java.util.HashMap[String, Object]() {
                     put("Content-Type", "text/html")
                     put("Server", Array("uWSGI", "Unbit"))
             }
             return Array(200:java.lang.Integer, headers , "Hello World")
     }
}

一旦用 scalac <filename> 编译好了,你可以像这样运行:

./uwsgi --socket /tmp/uwsgi.socket --plugins jvm,jwsgi --jwsgi HelloWorld:application --threads 40