Java擴展Nginx之一:你好,nginx-clojure

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

如果我說可以用java對nginx進行擴展,就像OpenResty支持Lua擴展一樣,您相信嗎?一起來體驗nginx-clojure吧 ...


歡迎訪問我的GitHub

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

關於Nginx擴展

  • 以欣宸自己為例,對一個java程式員來說,nginx並不陌生,下圖應該是最常見的使用場景了,反向代理:
    在這裡插入圖片描述
  • 除了反向代理,nginx的模塊化機制讓nginx具備了更多豐富的特性,例如ngx_http_log_module(日誌)、ngx_http_rewrite_module(重定向)等
  • 除了官方模塊,還有很多強大第三方模塊可以選擇,如下圖,更多詳情參考:https://www.nginx.com/resources/wiki/modules/
    在這裡插入圖片描述
  • 如此看來,在nginx上做二次開發並非神秘高深莫測,我們也可以按照自己的需要去定製和擴展nginx的功能
  • 然而,打開官方的開發文檔,畫風如下:
    在這裡插入圖片描述
  • nginx的自定義模塊使用C語言開發的,然而,欣宸只是個精通CRUD的Java程式員
  • 幸好有了nginx-clojure模塊,它支持使用Java對Nginx進行功能擴展:
    在這裡插入圖片描述
  • 至此,聰明的您應該猜到《Java擴展Nginx》系列的核心內容了:學習nginx-clojure模塊,從入門到精通,解鎖Clojure的強大能力,通過豐富的實戰與大家一起掌握用Java擴展Nginx的技術

本篇概覽

  • 作為《Java擴展Nginx》系列的開篇,本文將延續欣宸原創的風格:用最少的時間和最簡潔的功能,與大家一同對nginx-clojure模塊做個最基本的瞭解
  • 本文由以下部分構成:
  1. 介紹nginx-clojure
  2. 實戰
  3. 驗證

介紹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的介紹就到這裡吧,接下來實戰為主

實戰功能介紹

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

環境信息

  • 這裡給出我的實戰環境信息供您參考,這個環境可以正常運行所有實戰:
  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
#                  }
#               }
#          ';
#       }
#         
    }
}

7. 啟動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()

8. 驗證

  • 打開postman驗證服務是否正常,請求地址是http://127.0.0.1:8080/java
  • 響應如下圖所示,符合預期,返回的就是咱們定製的HelloHandler的內容
    在這裡插入圖片描述
  • 至此,nginx-clojure的入門操作就完成的,雖然寥寥幾行代碼,但卻給java程式員打開了一扇窗:用咱們熟悉的技術去擴展nginx,參與到nginx豐富的生態環境中

方向對嗎?個人的一點想法

  • 把java代碼寫在nginx上,讓nginx與業務耦合的可能性變大,經驗豐富的您一定會發現這是個危險的趨勢,畢竟nginx已經接近後臺服務的最外層了,穩定是首要考慮的,正常情況下,下圖這種將nginx作為反向代理,業務功能獨立部署的方式才是生產環境常用方案:
    在這裡插入圖片描述
  • 這麼看來,在nginx上寫java代碼是否合適呢?欣宸給不出權威答案,但是可以從大神的作品中得到靈感
  • 開濤大神的《跟我學Nginx+Lua開發》講述瞭如何用OpenResty在Nginx上用Lua進行開發,適用於web應用、接入網關、Web防火牆、緩存伺服器等場景下,並且在實體書《億級流量網站架構核心技術》也有詳細說明,例如京東商品詳情頁,就是在nginx上讀取緩存直接返回的
  • 綜上所訴,欣宸不會因為學習了這個技術,就把業務邏輯全部在nginx上實現,但是在緩存、鑒權等場景,可以考慮用熟悉的java在nginx上實現
  • 至此,《Java擴展Nginx》的開篇就完成了,簡簡單單的實戰,讓我們看到了java在nginx領域大顯神通的可能,接下來文章,會繼續深入學習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
更多相關文章
  • 一. 介紹 String、StringBuffer、StringBuilder: 前言: String、StringBuffer、StringBuilder 均在java.lang包下; String: 在Java中,String是一個特殊的引用類型,用於表示文本字元串。它提供了許多方法來操作和處理 ...
  • ## **Docker是什麼?** Docker是一個開源平臺,通過將應用程式隔離到輕量級、可移植的容器中,自動化應用程式的部署、擴展和管理。容器是獨立的可執行單元,封裝了運行應用程式所需的所有必要依賴項、庫和配置文件,可以在各種環境中穩定地運行。 # **什麼是容器?** 容器是一種輕量級、可移植 ...
  • - 背景: 經常創建和銷毀,使用量特別大的資源,比如併發情況下的線程,對性能影響很大。 - 思路:提前創建好多個線程,放入線程池中,使用時直接獲取,使用完放後池中。可以避免頻繁創建銷毀,實現重覆利用。類似生活中公共交通工具。 - 好處: - 提高響應速度(減少了創建新線程的時間) - 降低資源消耗( ...
  • **本文深入探討了 Django 中的請求與響應處理,從 Django 請求和響應的基礎知識、生命周期,到 HttpRequest 和 HttpResponse 對象的詳細介紹。同時,討論了 Django 的視圖和請求、響應處理,以及安全性和非同步處理的考慮。最後,對比了 Django 與 Flask ...
  • ### 三種創建方式 - Thread class 繼承Thread類 - Runnable介面 實現Runnable介面 - Callable介面 實現Callable介面 ### Thread - 自定義線程類繼承Thread類 - 重寫run()方法,編寫線程執行體 - 創建線程對象,調用st ...
  • python打包Windows.exe程式(pyinstaller) ## 基礎命令 `pip install pyinstaller` 使用pip命令來安裝pyinstaller模塊。 -F: `pyinstaller -F hello.py -p hello2.py` -D: `pyinstal ...
  • 編寫測試可以讓我們的代碼在後續迭代過程中不出現功能性缺陷問題;理解迭代器、閉包的函數式編程特性;`Box ...
  • 本文主要介紹如何通過 docker-maven-plugin 插件把 Java 服務構建成 docker 鏡像;文中所使用到的軟體版本:Docker 20.10.17、Java 1.8.0_341、SpringBoot 2.7.12、Maven 3.8.4、docker-maven-plugin 1 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...