作為servlet容器的hi-nginx-java

nginx發表於2020-11-07

hi-nginx-java是一個獨立於java官方的servlet規範,它有能力把NGINX直接編成servlet容器伺服器。換言之,無需安裝tomcat等容器伺服器,也無需使用nginx的反向代理功能,只需安裝jdk8+和hi-nginx,就能進行java web開發工作,而且效能更好。

 

先看NGINX的配置檔案部分:

hi_java_classpath "-Djava.class.path=.:/usr/local/nginx/java:/usr/local/nginx/java/hi-nginx-java.jar:/usr/local/nginx/java/jdemo.jar"

location ~ \.java {
            rewrite ^/(.*)\.java$ /$1 break;
            hi_need_kvdb on;
            hi_kvdb_size 50;
            hi_kvdb_expires 5m;
            hi_need_session on;
            hi_need_headers on;
            hi_need_cookies on;
            hi_java_servlet hi/jdemo;    
        }

應用是jdemo.jar。hi-nginx需要呼叫的入口servlet是hi.jdemo,配置時用/斜槓代替.點。該類的實現如下:

package hi;

import hi.servlet;
import hi.route;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.regex.Matcher;

public class jdemo implements hi.servlet {

    private static hi.route r = hi.route.get_instance();

    public jdemo() {
        jdemo.r.get("^/(hello|test)/?$", (hi.request req, hi.response res, Matcher m) -> {
            this.do_hello(req, res);
        });
        jdemo.r.get("^/error/?$", (hi.request req, hi.response res, Matcher m) -> {
            this.do_error(req, res);
        });
        jdemo.r.get("^/redirect/?$", (hi.request req, hi.response res, Matcher m) -> {
            this.do_redirect(req, res);
        });
        jdemo.r.add(new ArrayList<String>(Arrays.asList("GET", "POST")), "^/form/?$",
                (hi.request req, hi.response res, Matcher m) -> {
                    this.do_form(req, res);
                });
        jdemo.r.get("^/session/?$", (hi.request req, hi.response res, Matcher m) -> {
            this.do_session(req, res);
        });
        jdemo.r.get("^/md5/?$", (hi.request req, hi.response res, Matcher m) -> {
            this.do_md5(req, res);
        });
    }

    public void handler(hi.request req, hi.response res) {
        jdemo.r.run(req, res);
    }

    private void do_hello(hi.request req, hi.response res) {
        res.headers.get("Content-Type").set(0, "text/plain;charset=UTF-8");
        res.status = 200;
        res.content = "hello,world";
    }

    private void do_error(hi.request req, hi.response res) {
        res.headers.get("Content-Type").set(0, "text/plain;charset=UTF-8");
        res.status = 404;
        res.content = "404 Not found";
    }

    private void do_redirect(hi.request req, hi.response res) {
        res.status = 302;
        ArrayList<String> h = new ArrayList<String>();
        h.add("/hello.java");
        res.headers.put("Location", h);
    }

    private void do_form(hi.request req, hi.response res) {
        res.headers.get("Content-Type").set(0, "text/plain;charset=UTF-8");
        res.status = 200;
        StringBuilder buffer = new StringBuilder();

        buffer.append("head data " + req.headers.size() + "\n");
        buffer.append(this.do_foreach(req.headers));

        buffer.append("\ncookie data " + req.cookies.size() + "\n");
        buffer.append(this.do_foreach(req.cookies));

        buffer.append("\nform data " + req.form.size() + "\n");
        buffer.append(this.do_foreach(req.form));

        buffer.append(String.format("\nclient= %s\nmethod= %s\nuser_agent= %s\nuri= %s\nparam= %s\n", req.client,
                req.method, req.user_agent, req.uri, req.param));

        res.content = buffer.toString();
    }

    private void do_session(hi.request req, hi.response res) {
        res.headers.get("Content-Type").set(0, "text/plain;charset=UTF-8");
        res.status = 200;
        String key = "test";
        int value = 0;
        if (req.session.containsKey(key)) {
            value = Integer.parseInt(req.session.get(key)) + 1;
        }
        res.session.put(key, String.valueOf(value));
        res.content = String.format("hello,%d", value);
        res.status = 200;
    }

    private void do_md5(hi.request req, hi.response res) {
        res.headers.get("Content-Type").set(0, "text/plain;charset=UTF-8");
        String plaintext = "hello,md5!";
        res.status = 200;
        res.content = String.format("%s\nmd5= %s", plaintext, this.md5(plaintext));
    }

    private String md5(String str) {
        try {
            java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
            byte[] array = md.digest(str.getBytes());
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < array.length; ++i) {
                sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString();
        } catch (java.security.NoSuchAlgorithmException e) {
        }
        return null;
    }

    private String do_foreach(HashMap<String, String> m) {
        StringBuffer buffer = new StringBuffer();
        for (HashMap.Entry<String, String> item : m.entrySet()) {
            buffer.append(String.format("%s\t=\t%s\n", item.getKey(), item.getValue()));
        }
        return buffer.toString();
    }
}

將其用javac和jar編譯組裝為jdemo.jar檔案後,安裝至/usr/local/nginx/java目錄中。編寫好上述nginx配置,restart或者reload nginx即可通過訪問http://localhost/*.java即可獲得相應的服務。

hi.route的hi-nginx-java自帶的一個路由器,可實現類似python Flask框架的功能。如此,可大幅度省去tomcat應用釋出時繁瑣的配置過程,而且能夠獲得更好的應用效能。如果通過hi_need_cache開啟LRU快取,無論多短的快取時間,哪怕僅僅1秒,也能使得應用獲得近乎nginx靜態檔案服務一樣的效能表現,並且不會想tomcat等伺服器那樣大幅度消耗cpu和記憶體。

 

相關文章