OpenFaaS實戰之七:java11模板解析

程式設計師欣宸發表於2021-08-06

歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等;

OpenFaaS實戰系列文章連結

  1. 部署
  2. 函式入門
  3. Java函式
  4. 模板操作(template)
  5. 大話watchdog
  6. of-watchdog(為效能而生)
  7. java11模板解析
  8. OpenFaaS實戰之八:自制模板(maven+jdk8)
  9. OpenFaaS實戰之九:終篇,自制模板(springboot+maven+jdk8)

本篇概覽

  • 本文是《OpenFaaS實戰》系列的第七篇,經過前面的知識儲備,我們們對OpenFaaS的服務呼叫和容器執行原理已經瞭然於胸,可以更深入的研究和使用了OpenFaaS了;
  • 想要更加自由的開發函式,加入更多符合業務需要的特性,顯然官方提供的幾個模板是無法滿足我們們的需要,以欣宸熟悉的Java為例,現有的java11java11-vert-x存在以下問題:
  1. 是基於Gradle的,而實際上習慣使用Maven的開發者並不少;
  2. 沒有Spring、SpringBoot;
  3. 不支援類似dubbo、SpringCloud等分散式呼叫;
  • 綜上所述,java程式設計師常用的技術棧很難在OpenFaaS的官方模板得到支援,沒關係,我們們可以自己開發模板支援上述能力,不過這不是本章的任務,本章的目標是一起深入瞭解java11模板,摸清官方套路,為後面的自定義模板開發做好充分的準備,本篇文章有以下內容:
  1. 解析Dockerfile
  2. Java原始碼學習
  • 沒錯,java11模板很簡單,很快就能瞭解其中原理;

解析Dockerfile

  • 回顧of-watchdog的http模式內部架構,如下圖:

在這裡插入圖片描述

  • 從上圖可見函式功能程式碼能被呼叫的關鍵有以下兩點:
  1. 有微服務(child)在監聽指定埠;
  2. of-watchdog(parent)收到外部請求會轉發到微服務監聽的埠;
  • 最為關鍵的微服務和of-watchdog都聚集在同一個docker容器中,因此該docker映象的Dockerfile檔案就是一切的關鍵,接下來一起看看這個檔案;
  • 在OpenFaaS環境執行命令faas template pull可以拉取全部官方模板,在template/java11目錄下是該模板的全部檔案:
[root@node1 template]# tree java11
java11
├── build.gradle
├── Dockerfile
├── function
│   ├── build.gradle
│   ├── gradle
│   │   └── wrapper
│   │       ├── gradle-wrapper.jar
│   │       └── gradle-wrapper.properties
│   ├── gradlew
│   ├── gradlew.bat
│   ├── settings.gradle
│   └── src
│       ├── main
│       │   └── java
│       │       └── com
│       │           └── openfaas
│       │               └── function
│       │                   └── Handler.java
│       └── test
│           └── java
│               └── HandlerTest.java
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── README.md
├── settings.gradle
└── template.yml
  • 開啟Dockerfile閱讀,我在指令碼的關鍵位置新增了註釋輔助理解,如下所示:
# 使用multi-stage builds特性,將整個映象構建分為多個階段
# 名為builder的映象裡面會生成java程式碼編譯構建出來的結果
FROM openjdk:11-jdk-slim as builder

ENV GRADLE_VER=6.1.1
# 應用更新,並且安裝後面要用到的應用
RUN apt-get update -qqy \
  && apt-get install -qqy \
   --no-install-recommends \
   curl \
   ca-certificates \
   unzip

# 下載指定版本的gradle,並解壓,再刪除壓縮包(避免映象體積變大)
RUN mkdir -p /opt/ && cd /opt/ \
    && echo "Downloading gradle.." \
    && curl -sSfL "https://services.gradle.org/distributions/gradle-${GRADLE_VER}-bin.zip" -o gradle-$GRADLE_VER-bin.zip \
    && unzip gradle-$GRADLE_VER-bin.zip -d /opt/ \
    && rm gradle-$GRADLE_VER-bin.zip

# Export some environment variables
ENV GRADLE_HOME=/opt/gradle-$GRADLE_VER/
ENV PATH=$PATH:$GRADLE_HOME/bin

RUN mkdir -p /home/app/libs

ENV GRADLE_OPTS="-Dorg.gradle.daemon=false"
WORKDIR /home/app

# 把編譯構建涉及的所有內容都複製到映象的/home/app/目錄,
# 包括配置檔案、java原始碼等
COPY . /home/app/

# 開始編譯構建
RUN gradle build

# 列印檔案列表
RUN find . 

# 名為watchdog的映象,注意基礎映象是openfaas/of-watchdog
FROM openfaas/of-watchdog:0.7.6 as watchdog

# 這個ship才是最終的映象,前面的builder和watchdog都是為ship準備內容的
# 為了控制體積,ship裡面是jre,而非jdk
FROM openjdk:11-jre-slim as ship
RUN apt-get update -qqy \
  && apt-get install -qqy \
   --no-install-recommends \
   unzip

# 為了安全起見不使用root帳號,這裡增加名為app的群組和使用者
RUN addgroup --system app \
    && adduser --system --ingroup app app

# 從watchdog映象獲取可執行檔案fwatchdog
COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog

# 增加可執行許可權
RUN chmod +x /usr/bin/fwatchdog

# 設定執行命令的目錄
WORKDIR /home/app

# 從builder獲取整個gradle專案的構建結果
COPY --from=builder /home/app/function/build/distributions/function-1.0.zip ./function-1.0.zip

# 執行執行容器程式的帳號是app
user app

# 解壓構建結果
RUN unzip ./function-1.0.zip

WORKDIR /home/app/

# of-watchdog轉發的地址,也就是微服務監聽的地址
ENV upstream_url="http://127.0.0.1:8082"
# of-watchdog的模式
ENV mode="http"

# 微服務是java應用,要用到這個classpath
ENV CLASSPATH="/home/app/function-1.0/function-1.0.jar:/home/app/function-1.0/lib/*"

# 啟動微服務的命令
ENV fprocess="java -XX:+UseContainerSupport com.openfaas.entrypoint.App"

# 對外暴露的埠,of-watchdog監聽的
EXPOSE 8080

# 監控檢查
HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1

# 容器啟動時執行的命令,既啟動of-watchdog
CMD ["fwatchdog"]

在這裡插入圖片描述

  • 為了更清晰的看到指令碼中三個任務是如何協同的,將整個Dockerfile的指令碼用下圖表示,可見最終的映象來自ship,左側的builderwatchdog都是為ship提供內容的:

在這裡插入圖片描述

java工程分析

  1. 從Dockerfile中得知微服務的啟動命令如下:
java -XX:+UseContainerSupport com.openfaas.entrypoint.App
  1. 只要搞清楚上述命令對應的實現,整個java11模板就全部掌握了,接下來就來研究這個com.openfaas.entrypoint.App類;
  2. 開啟檔案template/java11/function/build.gradle,看到依賴關係如下圖,紅框中的庫應該就是com.openfaas.entrypoint.App的來源了:

在這裡插入圖片描述

  1. 上圖紅框中的庫,程式碼已經開源,地址是:https://github.com/openfaas/templates-sdk/tree/master/java11
  2. 開啟App.java檔案後,一切謎底都被揭開了,這個java11模板的原始碼還真是簡單呀,先看入口的main方法:
public static void main(String[] args) throws Exception {
        // 監聽8082埠,和Dockerfile中of-watchdog轉發請求的埠保持一致
        int port = 8082;
		
		// handler是真正處理請求的例項
        HandlerProvider p = HandlerProvider.getInstance();
        IHandler handler = p.getHandler();
		
		// 配置監聽物件,並將handler繫結過來
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
        InvokeHandler invokeHandler = new InvokeHandler(handler);

		// 設定path,開始監聽
        server.createContext("/", invokeHandler);
        server.setExecutor(null); // creates a default executor
        server.start();
    }
  1. 有沒有覺得上述程式碼和sockek程式設計很像;
  2. App.java中還有靜態類InvokeHandler的定義,這個類的主要功能就是接收web請求的資料,加工成IRequest例項,丟給IHandler例項去處理,處理完成後包裝http響應,核心程式碼片段如下:
// 把request內容封裝到IRequest例項中
IRequest req = new Request(requestBody, reqHeadersMap,t.getRequestURI().getRawQuery(), t.getRequestURI().getPath());

// 執行業務邏輯處理請求            
IResponse res = this.handler.Handle(req);

// 得到處理後的結果
String response = res.getBody();
...
  1. 上述程式碼可見業務邏輯的執行的關鍵是this.handler.Handle(req),而真正執行業務的程式碼是我們們自己寫的Handler.java,要搞清楚它們之間的關係,就要看HandlerProvider.java,如下圖,一些都清楚了,我們們開發函式時,編寫的業務功能都在Handler.java中,而Handler是AbstractHandler的實現類,於是下圖紅框1中就會找到Handler,紅框2可以返回Handler例項,在InvokeHandler執行this.handler.Handle(req)時,就是Handler例項在處理web請求了:

在這裡插入圖片描述

  1. 至此,java程式碼的分析就完成了,這個微服務其實很簡單,就像我們們做Socket程式設計練習那樣,自己編碼監聽埠並編寫處理邏輯;

小結

最後做個小結,將前面展開的思路收斂起來,如下圖:

在這裡插入圖片描述

看到這裡,對於java11模板的內部實現及其執行原理,相信在您眼裡應該沒有什麼祕密了,為了製作更好用的java模板,我們們已經做了充分準備,接下來的文章,請隨欣宸一起實戰自定義java模板;

你不孤單,欣宸原創一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 資料庫+中介軟體系列
  6. DevOps系列

歡迎關注公眾號:程式設計師欣宸

微信搜尋「程式設計師欣宸」,我是欣宸,期待與您一同暢遊Java世界...
https://github.com/zq2599/blog_demos

相關文章