精選版:用Java擴展Nginx(nginx-clojure 入門)

来源:https://www.cnblogs.com/bolingcavalry/archive/2023/09/07/17673531.html
-Advertisement-
Play Games

讓 Java 代碼直接在 Nginx 上運行?這麼有趣的功能,隨本文一起來實戰體驗吧,圖文並茂,一定能成功的那種實戰 ...


歡迎訪問我的GitHub

這裡分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

本篇概覽

  • 今天咱們以Java程式員的視角,來聊聊如何用Java來擴展Nginx的能力,全文由以下內容組成:
  1. 什麼是Nginx
  2. 什麼是Nginx擴展
  3. 擴展Nginx,意義何在?
  4. 想用Java擴展Nginx會有問題嗎?
  5. 主角登場:nginx-clojure模塊,讓Java擴展Nginx成為顯示
  • 總的來說這是一篇從概念到實踐的入門文章,接下來,一起開始愉快的nginx-clojure之旅吧!

關於Nginx

  • 以欣宸自己為例,nginx對java程式員來說並不陌生,下圖應該是最常見的使用場景了:反向代理
    在這裡插入圖片描述

關於Nginx擴展

  • 除了反向代理,nginx的模塊化機制讓nginx具備了更多豐富的特性,例如ngx_http_log_module(日誌)、ngx_http_rewrite_module(重定向)等
  • 除了官方模塊,還有很多強大第三方模塊可以選擇,如下圖,更多詳情參考:https://www.nginx.com/resources/wiki/modules/
    在這裡插入圖片描述
  • 大名鼎鼎的OpenResty就是基於 Nginx 擴展的,用Lua做二次開發web功能,得到了廣泛的應用
  • 如此看來,在nginx上做二次開發並非神秘高深莫測,我們也可以按照自己的需要去定製和擴展nginx的功能
  • 作為一名Java程式員,自然就想到了:可以用Java去擴展Nginx的能力嗎?

為什麼要用java對nginx擴展?

  • 把java代碼寫在nginx上,讓nginx與業務耦合的可能性變大,經驗豐富的您一定會發現這是個危險的趨勢,畢竟nginx已經接近後臺服務的最外層了,穩定是首要考慮的,正常情況下,下圖這種用法才是生產環境的常見fang方案:將nginx作為反向代理,業務功能獨立部署
    在這裡插入圖片描述
  • 這麼看來,在nginx上寫java代碼是否合適呢?欣宸給不出權威答案,但是可以從大神的作品中得到啟示
  • 開濤大神的《跟我學Nginx+Lua開發》講述瞭如何用OpenResty在Nginx上用Lua進行開發,適用於web應用、接入網關、Web防火牆、緩存伺服器等場景下,並且在實體書《億級流量網站架構核心技術》也有詳細說明,例如京東商品詳情頁,就是在nginx上讀取緩存直接返回的
  • 綜上所述,欣宸不會因為學習了這個技術,就把業務邏輯全部在nginx上實現,但是在緩存、鑒權等場景,可以考慮用熟悉的java在nginx上實現

Java程式員的尷尬

  • 定製Nginx,打造高性能Web服務,帶著這個美好憧憬,我打開了有關模塊開發的Nginx官方資料
  • 然後,畫風開始劇變,而且是朝著我不想面對的方向,如下圖
    在這裡插入圖片描述
  • 模塊開發用的居然是C語言!然而,欣宸只是個精通CRUD的Java程式員
  • 這可怎麼辦?隨手打開一個網頁就讓夢想破滅?

nginx-clojure,讓Java擴展nginx成為現實

  • 經過一番搜索,終於找到了nginx-clojure模塊,如下圖,是它讓java擴展nginx成為現實
    在這裡插入圖片描述
  • nginx-clojure的作用很好理解:OpenResty支持用Lua擴展nginx,nginx-clojure支持用Java擴展nginx
  • nginx-clojure的作用很好理解:OpenResty支持用Lua擴展nginx,nginx-clojure支持用Java擴展nginx

關於nginx-clojure

  • nginx-clojure是個第三方Nginx模塊,官方的介紹是Nginx module for embedding Clojure / Java / Groovy programs, typically those Ring based handlers
  • nginx-clojure模塊支持嵌入式Clojure(閉包)、Java、Groovy等基於Ring的處理器(handler),那什麼是Ring呢?
  • Ring 在 Clojure 中是一個構建 Web 應用的底層介面和庫. 它和 Ruby 的 Rack, Python 裡面的WSGI 或者 Java Servlet 規範相似
  • 從java開發者角度來看,就是開發NginxJavaRingHandler的實現類,然後該類可以在nginx-clojure模塊中被運行
  • nginx-clojure的最新版本是v0.5.2,官網地址是:https://nginx-clojure.github.io

實戰功能介紹

  • 儘管nginx-clojure支持很多功能,但是篇幅所限,因此本篇以瞭解為主,還是經典的Hello world,更多精彩內容留給後續的系列文章
  • 今天的實戰效果如下圖,咱們編寫HelloHandler.java併在nginx做好配置,然後用瀏覽器發起請求後,HelloHandler的代碼就會被執行,瀏覽器會收到HelloHandler返回的內容,這就證實了java代碼可以在nginx上運行:
    在這裡插入圖片描述
  • 為了實現上述功能,接下來的操作步驟如下圖:
    在這裡插入圖片描述
  • 感謝您聽我嘮叨了這麼久,接下來,實戰開始

環境信息

  • 這裡給出我的實戰環境信息供您參考,這個環境可以正常運行所有實戰:
  1. 操作系統:macOS Big Sur 11.5.2 (20G95)
  2. JDK:1.8.0_281
  3. Maven:3.8.1

下載集成了nginx-clojure模塊的nginx包

  • 咱們要做的第一件事是下載一個特別的nginx,之所以說它特別,是因為它已集成了nginx-clojure模塊,開箱即用
  • 下載地址:https://sourceforge.net/projects/nginx-clojure/files/ ,如下圖紅框,我這裡選擇的是最新的0.5.2版本:
    在這裡插入圖片描述

解壓nginx包

  • 下載完畢後,解壓,得到名為nginx-clojure-0.5.2的文件夾,裡面的內容如下
    在這裡插入圖片描述
  • 接下來根據您的操作系統對可執行文件做重命名,我這邊是macOS,所以把nginx-macosx重命名為nginx,如果是linux,把nginx-linux-64重命名為nginx,如果是windows,就把nginx-win64.exe重命名為nginx.exe
  • 上述重命名操作是nginx-clojure官方推薦的,統一可執行文件名,這樣運行文檔中的命令就統一了
  • 執行命令./nginx -v,控制台響應如下,可見nginx版本是1.18.0:
./nginx -v
nginx version: nginx/1.18.0

編碼,開發java版handler

  • 接下來開始寫代碼,先新建一個maven工程(我這裡名叫simple-hello),pom.xml中需要配置repository節點,以及唯一的依賴nginx-clojure,如下所示:
<repositories>
  <repository>
    <id>clojars.org</id>
    <url>http://clojars.org/repo</url>
    </repository>
</repositories>

<dependencies>
  <dependency>
    <groupId>nginx-clojure</groupId>
    <artifactId>nginx-clojure</artifactId>
    <version>0.5.2</version>
  </dependency>
</dependencies>
  • 然後新增文件HelloHandler.java,如下所示,代碼非常簡單,實現NginxJavaRingHandler介面,invoke方法返回的數組中只有三個元素:返回碼、響應header的鍵值對集合、響應body內容:
package com.bolingcavalry.simplehello;

import nginx.clojure.java.ArrayMap;
import nginx.clojure.java.NginxJavaRingHandler;

import java.time.LocalDateTime;
import java.util.Map;

import static nginx.clojure.MiniConstants.CONTENT_TYPE;
import static nginx.clojure.MiniConstants.NGX_HTTP_OK;

/**
 * @author [email protected]
 * @Title: 產生內容的handler
 * @Package
 * @Description:
 * @date 2/1/22 12:41 PM
 */
public class HelloHandler implements NginxJavaRingHandler {

    @Override
    public Object[] invoke(Map<String, Object> request) {
        return new Object[] {
                NGX_HTTP_OK, //http status 200
                ArrayMap.create(CONTENT_TYPE, "text/plain"), //headers map
                "Hello, Nginx clojure! " + LocalDateTime.now()  //response body can be string, File or Array/Collection of them
        };
    }
}
  • 至此,編碼完成,欣宸精通Hello World果然並非空穴來風…

編譯,生成jar

  • 在pom.xml所在目錄執行編譯構建的命令mvn clean package -U,會在target目錄下生成jar包,只有3K大小:
    在這裡插入圖片描述

jar放入nginx的jars目錄

  • 將前面生成的simple-hello-1.0-SNAPSHOT.jar文件放入下圖紅框的jars文件夾內
    在這裡插入圖片描述

修改nginx的配置

  • 打開nginx-clojure-0.5.2/conf/nginx.conf文件,在server配置中增加一個location配置,內容如下,指定了handler類型,以及對應的java類:
location /java {
         content_handler_type 'java';
         content_handler_name 'com.bolingcavalry.simplehello.HelloHandler';
}
  • 修改後,完整的nginx.conf內容如下,可見有很多java相關的配置,在本篇咱們都保持不動,留待後面的文章解鎖:
###you can uncomment next two lines for easy debug
###Warning: if master_process is off, there will be only one nginx worker running. Only use it for debug propose.

#daemon  off;

#master_process  off;

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
    

    jvm_path auto;
    
    ### Set my app jars and resources, it must include nginx-clojure runtime jar,e.g. nginx-clojure-0.5.1.jar and 
    ### for clojure user clojure runtime jar is also needed.
    ### See http://nginx-clojure.github.io/directives.html#jvm_classpath
    jvm_classpath "libs/*:jars/*";
    
    ###jvm heap memory
    #jvm_options "-Xms1024m";
    #jvm_options "-Xmx1024m";
    
    #for enable java remote debug uncomment next two lines
    #jvm_options "-Xdebug";
    #jvm_options "-Xrunjdwp:server=y,transport=dt_socket,address=840#{pno},suspend=n";

    ###threads number for request handler thread pool on jvm, default is 0. 
    ###check more details from 
    #jvm_workers 8;

    server {
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

       location /clojure {
          handler_type 'clojure';
          handler_code ' 
						(fn[req]
						  {
						    :status 200,
						    :headers {"content-type" "text/plain"},
						    :body  "Hello Clojure & Nginx!" 
						    })
          ';
       }

       location /java {
         content_handler_type 'java';
         content_handler_name 'com.bolingcavalry.simplehello.HelloHandler';
       }
       
#      location /groovy {
#          handler_type 'groovy';
#          handler_code ' 
#               import nginx.clojure.java.NginxJavaRingHandler;
#               import java.util.Map;
#               public class HelloGroovy implements NginxJavaRingHandler {
#                  public Object[] invoke(Map<String, Object> request){
#                     return [200, //http status 200
#                             ["Content-Type":"text/html"], //headers map
#                             "Hello, Groovy & Nginx!"]; //response body can be string, File or Array/Collection of them
#                  }
#               }
#          ';
#       }
#         
    }
}

啟動nginx

  • 啟動命令很簡單,在nginx-clojure-0.5.2目錄下執行./nginx
  • 如果啟動失敗了,請打開nginx-clojure-0.5.2/logs/error.log查看問題,例如我這裡遇到過埠占用導致啟動失敗:
2022/02/02 17:45:07 [emerg] 27703#0: bind() to 0.0.0.0:8080 failed (48: Address already in use)
2022/02/02 17:45:07 [emerg] 27703#0: bind() to 0.0.0.0:8080 failed (48: Address already in use)
2022/02/02 17:45:07 [emerg] 27703#0: bind() to 0.0.0.0:8080 failed (48: Address already in use)
2022/02/02 17:45:07 [emerg] 27703#0: bind() to 0.0.0.0:8080 failed (48: Address already in use)
2022/02/02 17:45:07 [emerg] 27703#0: bind() to 0.0.0.0:8080 failed (48: Address already in use)
2022/02/02 17:45:07 [emerg] 27703#0: still could not bind()

驗證

  • 打開postman驗證服務是否正常,請求地址是http://127.0.0.1:8080/java
  • 響應如下圖所示,符合預期,返回的就是咱們定製的HelloHandler的內容
    在這裡插入圖片描述
  • 至此,nginx-clojure的入門操作就完成了,雖然寥寥幾行代碼,但卻給java程式員打開了一扇窗:用咱們熟悉的技術去擴展nginx,打造更符合業務的web伺服器,以最短鏈路完成web響應
  • 本篇只是nginx-clojure之旅的開端,先對nginx-clojure有個清晰的認識,接下來的文章咱們會深度探索它,讓它更好的服務於業務

源碼下載

名稱 鏈接 備註
項目主頁 https://github.com/zq2599/blog_demos 該項目在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該項目源碼的倉庫地址,https協議
git倉庫地址(ssh) [email protected]:zq2599/blog_demos.git 該項目源碼的倉庫地址,ssh協議
  • 這個git項目中有多個文件夾,本篇的源碼在nginx-clojure-tutorials文件夾下,如下圖紅框所示:
    在這裡插入圖片描述

歡迎關註博客園:程式員欣宸

學習路上,你不孤單,欣宸原創一路相伴...


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • `` 數組的includes方法在日常的編程中比較常用到,其作用就是判斷某一數據是否在數組中,通常來說,數組中的數據如果是數字,布爾值,或者字元串的話,都是能夠進行判斷的 例如: ``` [1,2,3,4].includes(3) // true [1,2,3,4].includes(5) // f ...
  • 好家伙, 1.<template>去哪了 在正式內容之前,我們來思考一個問題, 當我們使用vue開發頁面時,<tamplete>中的內容是如何變成我們網頁中的內容的? 它會經歷四步: 解析模板:Vue會解析<template>中的內容,識別出其中的指令、插值表達式({{}}),以及其他元素和屬性。 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一.@click和@click.native的區別 vue @click.native 原生點擊事件: 1,給vue組件綁定事件時候,必須加上native ,不然不會生效(監聽根元素的原生事件,使用 .native 修飾符) 2,等同於在 ...
  • 在vue3中,可以使用vue3的API `defineExpose()`函數結合`ref`或者`$parent`,實現父子組件數據的傳遞。 # 子組件向父組件傳遞數據`defineExpose()`和`ref` - 子組件:通過`defineExpose()` 函數,向外暴露響應式數據或者方法 `` ...
  • 本文給大家介紹了什麼是"編程範式",選擇合適的編程範式可以提高代碼的可讀性、可維護性和可擴展性。 一、 什麼是編程範式? "編程範式"是一種編程思想的總稱,它是指在編寫程式時所採用的基本方法和規範。常見的編程範式有面向對象、函數式、邏輯式等。 選擇合適的編程範式可以提高代碼的可讀性、可維護性和可擴展 ...
  • ### 工廠模式 工廠模式是一種創建者設計模式,細分之下可以分成三類`簡單工廠模式`,`工廠方法模式`和`抽象工廠模式`。 #### 簡單工廠模式 最簡單的工廠模式,它採用靜態方法的方式來決定應該應該生產什麼商品。 ```java public class StoreFactory { public ...
  • 本文與大家一起學習並介紹領域驅動設計(Domain Drive Design) 簡稱DDD,以及為什麼我們需要領域驅動設計,它有哪些優缺點,儘量用一些通俗易懂文字來描述講解領域驅動設計 ...
  • 這篇文章的主要內容包括:1、數據架構的演變歷史與各種架構的優缺點。2、流批一體的價值。3、流批一體架構中流與批的關係。 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...