OpenFaaS實戰之七:java11模板解析

歡迎訪問我的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爲例,現有的<font color="blue">java11</font>和<font color="blue">java11-vert-x</font>存在以下問題:
  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環境執行命令<font color="blue">faas template pull</font>可以拉取全部官方模板,在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
  • 打開<font color="blue">Dockerfile</font>閱讀,我在腳本的關鍵位置添加了註釋輔助理解,如下所示:
# 使用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的腳本用下圖表示,可見最終的鏡像來自<font color="blue">ship</font>,左側的<font color="blue">builder</font>和<font color="blue">watchdog</font>都是爲ship提供內容的:

在這裏插入圖片描述

java工程分析

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

在這裏插入圖片描述

  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中還有靜態類<font color="blue">InvokeHandler</font>的定義,這個類的主要功能就是接收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. 上述代碼可見業務邏輯的執行的關鍵是<font color="blue">this.handler.Handle(req)</font>,而真正執行業務的代碼是咱們自己寫的Handler.java,要搞清楚它們之間的關係,就要看HandlerProvider.java,如下圖,一些都清楚了,咱們開發函數時,編寫的業務功能都在Handler.java中,而Handler是AbstractHandler的實現類,於是下圖紅框1中就會找到Handler,紅框2可以返回Handler實例,在InvokeHandler執行<font color="blue">this.handler.Handle(req)</font>時,就是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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章