歡迎訪問我的GitHub
https://github.com/zq2599/blog_demos
內容:所有原創文章分類匯總及配套源碼,涉及Java、Docker、Kubernetes、DevOPS等;
OpenFaaS實戰系列文章鏈接
- 部署
- 函數入門
- Java函數
- 模板操作(template)
- 大話watchdog
- of-watchdog(為性能而生)
- java11模板解析
- OpenFaaS實戰之八:自制模板(maven+jdk8)
- OpenFaaS實戰之九:終篇,自制模板(springboot+maven+jdk8)
本篇概覽
- 本文是《OpenFaaS實戰》系列的第七篇,經過前面的知識儲備,咱們對OpenFaaS的服務調用和容器運行原理已經了然於胸,可以更深入的研究和使用了OpenFaaS了;
- 想要更加自由的開發函數,加入更多符合業務需要的特性,顯然官方提供的幾個模板是無法滿足咱們的需要,以欣宸熟悉的Java為例,現有的java11和java11-vert-x存在以下問題:
- 是基於Gradle的,而實際上習慣使用Maven的開發者並不少;
- 沒有Spring、SpringBoot;
- 不支持類似dubbo、SpringCloud等分布式調用;
- 綜上所述,java程序員常用的技術棧很難在OpenFaaS的官方模板得到支持,沒關系,咱們可以自己開發模板支持上述能力,不過這不是本章的任務,本章的目標是一起深入了解java11模板,摸清官方套路,為后面的自定義模板開發做好充分的准備,本篇文章有以下內容:
- 解析Dockerfile
- Java源碼學習
- 沒錯,java11模板很簡單,很快就能了解其中原理;
解析Dockerfile
- 回顧of-watchdog的http模式內部架構,如下圖:
- 從上圖可見函數功能代碼能被調用的關鍵有以下兩點:
- 有微服務(child)在監聽指定端口;
- 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"]
- 上述腳本的最后設置了好幾個環境變量,這些環境變量的定義來自of-watchdog官方Github,地址是:https://github.com/openfaas/of-watchdog ,內容如下圖:
- 為了更清晰的看到腳本中三個任務是如何協同的,將整個Dockerfile的腳本用下圖表示,可見最終的鏡像來自ship,左側的builder和watchdog都是為ship提供內容的:
java工程分析
- 從Dockerfile中得知微服務的啟動命令如下:
java -XX:+UseContainerSupport com.openfaas.entrypoint.App
- 只要搞清楚上述命令對應的實現,整個java11模板就全部掌握了,接下來就來研究這個com.openfaas.entrypoint.App類;
- 打開文件template/java11/function/build.gradle,看到依賴關系如下圖,紅框中的庫應該就是com.openfaas.entrypoint.App的來源了:
- 上圖紅框中的庫,代碼已經開源,地址是:https://github.com/openfaas/templates-sdk/tree/master/java11
- 打開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();
}
- 有沒有覺得上述代碼和sockek編程很像;
- 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();
...
- 上述代碼可見業務邏輯的執行的關鍵是this.handler.Handle(req),而真正執行業務的代碼是咱們自己寫的Handler.java,要搞清楚它們之間的關系,就要看HandlerProvider.java,如下圖,一些都清楚了,咱們開發函數時,編寫的業務功能都在Handler.java中,而Handler是AbstractHandler的實現類,於是下圖紅框1中就會找到Handler,紅框2可以返回Handler實例,在InvokeHandler執行this.handler.Handle(req)時,就是Handler實例在處理web請求了:
- 至此,java代碼的分析就完成了,這個微服務其實很簡單,就像咱們做Socket編程練習那樣,自己編碼監聽端口並編寫處理邏輯;
小結
最后做個小結,將前面展開的思路收斂起來,如下圖:
看到這里,對於java11模板的內部實現及其執行原理,相信在您眼里應該沒有什么秘密了,為了制作更好用的java模板,咱們已經做了充分准備,接下來的文章,請隨欣宸一起實戰自定義java模板;
你不孤單,欣宸原創一路相伴
歡迎關注公眾號:程序員欣宸
微信搜索「程序員欣宸」,我是欣宸,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos