資料庫連線池到底應該設多大?
歡迎訪問我的GitHub
github.com/zq2599/blog_demos
內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等;
本篇概覽
- 本文是《OpenFaaS實戰》系列的第八篇,經過前面的理論分析和實戰練習,我們們對OpenFaaS瞭解得差不多了,也該搞事情了;
- 作為一個Java程式設計師,經常用到jdk8、maven、springboot這些東西,自然要關注官方模板是否支援,如下圖,顯示對java程式設計師的支援力度不夠:不支援java8、用的是Gradle而非maven、不支援springboot,僅用來支援web服務:
-
既然官方模板不支援,我們們就自制模板來支援吧,本著先易後難的原則,本篇先做一個簡單的模板:將官方的java11模板保持功能不變,jdk版本改造成java8,並將Gradle改成maven;
-
不可否認jdk8和maven都已一大把年紀了,新版jdk和Gradle都是更好的選擇,不過本篇的重點是如何自定義模板,所以還請您給予包容…
-
今天要做的事情,如下圖所示,我們們先做左邊藍色部分,編寫模板程式碼,上傳到github模板倉庫,再做右側綠色部分,像前面文章中使用官方模板那樣去使用這個模板:
- 接下來的實戰由以下內容組成:
- 建立java專案,作為模板的基礎原始碼
- 開發Dockerfile
- 完成模板配置並上傳
- 驗證模板
建立java專案
- 製作模板時最重要的就是提供完整的模板程式碼,接下來就來製作吧;
- 我這邊用的是IDEA,建一個空maven專案,名為java8maven,用的是JDK8:
- 如下圖,注意Language level要選擇8:
- pom.xml的內容如下,要注意的幾個點稍後會說明:
project xmlns=""
xmlns:xsi=""
xsi:schemaLocation=" ">
modelVersion>4.0.0modelVersion>
groupId>com.bolingcavalrygroupId>
artifactId>java8mavenartifactId>
version>1.0-SNAPSHOTversion>
properties>
project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
java.version>1.8java.version>
properties>
dependencies>
dependency>
groupId>org.apache.commonsgroupId>
artifactId>commons-math3artifactId>
version>3.6.1version>
dependency>
dependency>
groupId>com.google.guavagroupId>
artifactId>guavaartifactId>
version>23.0version>
dependency>
dependency>
groupId>junitgroupId>
artifactId>junitartifactId>
version>4.12version>
dependency>
dependency>
groupId>com.openfaasgroupId>
artifactId>modelartifactId>
version>0.1.1version>
dependency>
dependency>
groupId>com.openfaasgroupId>
artifactId>entrypointartifactId>
version>0.1.0version>
dependency>
dependency>
groupId>com.fasterxml.jackson.coregroupId>
artifactId>jackson-databindartifactId>
version>2.11.0version>
dependency>
dependency>
groupId>org.apache.commonsgroupId>
artifactId>commons-lang3artifactId>
version>3.10version>
dependency>
dependencies>
build>
plugins>
plugin>
groupId>org.apache.maven.pluginsgroupId>
artifactId>maven-compiler-pluginartifactId>
version>3.1version>
configuration>
source>1.8source>
target>1.8target>
encoding>UTF-8encoding>
configuration>
plugin>
plugin>
groupId>org.apache.maven.pluginsgroupId>
artifactId>maven-dependency-pluginartifactId>
version>2.10version>
executions>
execution>
id>copy-dependenciesid>
phase>packagephase>
goals>
goal>copy-dependenciesgoal>
goals>
configuration>
outputDirectory>${project.build.directory}/liboutputDirectory>
overWriteReleases>falseoverWriteReleases>
overWriteSnapshots>falseoverWriteSnapshots>
overWriteIfNewer>trueoverWriteIfNewer>
configuration>
execution>
executions>
plugin>
plugin>
artifactId>maven-assembly-pluginartifactId>
version>3.0.0version>
configuration>
archive>
manifest>
mainClass>com.openfaas.entrypoint.AppmainClass>
manifest>
manifestEntries>
Class-Path>.Class-Path>
manifestEntries>
archive>
descriptorRefs>
descriptorRef>jar-with-dependenciesdescriptorRef>
descriptorRefs>
configuration>
executions>
execution>
id>make-assemblyid>
phase>packagephase>
goals>
goal>singlegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
project>
- 上述pom.xml的內容中,有幾處需要注意:
- openfaas的model和entrypoint這兩個jar是整個服務可執行的基礎;
- 有些常用的jar依賴也被加入了,您可以酌情自行增刪;
- 外掛maven-compiler-plugin用來指定編譯時的JDK版本;
- 外掛maven-dependency-plugin和maven-assembly-plugin用來將整個java程式碼和依賴庫打包到一個jar檔案中,這樣製作Docker映象會方便很多;
- 新建一個java類:com.openfaas.function.Handler,原始碼和[《OpenFaaS實戰之三:Java函式》]中的Handler.java一模一樣,如下:
package com.openfaas.function;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.openfaas.model.IRequest;
import com.openfaas.model.IResponse;
import com.openfaas.model.Response;
import org.apache.commons.lang3.StringUtils;
import java.lang.management.ManagementFactory;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class Handler extends com.openfaas.model.AbstractHandler {
private static final String PARAM_USER_NAME = "name";
private static final String RESPONSE_TEMPLETE = "Hello %s, response from [%s], PID [%s], %s";
private ObjectMapper mapper = new ObjectMapper();
/**
* 獲取本機IP地址
* @return
*/
public static String getIpAddress() {
try {
EnumerationNetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();
InetAddress ip = null;
while (allNetInterfaces.hasMoreElements()) {
NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
if (netInterface.isLoopback() || netInterface.isVirtual() || !netInterface.isUp()) {
continue;
} else {
EnumerationInetAddress> addresses = netInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
ip = addresses.nextElement();
if (ip != null && ip instanceof Inet4Address) {
return ip.getHostAddress();
}
}
}
}
} catch (Exception e) {
System.err.println("IP地址獲取失敗" + e.toString());
}
return "";
}
/**
* 返回當前程式ID
* @return
*/
private static String getPID() {
return ManagementFactory
.getRuntimeMXBean()
.getName()
.split("@")[0];
}
private String getUserName(IRequest req) {
// 如果從請求body中取不到userName,就用
String userName = null;
try {
MapString, Object> mapFromStr = mapper.readValue(req.getBody(),
new TypeReferenceMapString, Object>>() {});
if(null!=mapFromStr && mapFromStr.containsKey(PARAM_USER_NAME)) {
userName = String.valueOf(mapFromStr.get(PARAM_USER_NAME));
}
} catch (Exception e) {
e.printStackTrace();
}
// 如果從請求body中取不到userName,就給個預設值
if(StringUtils.isBlank(userName)) {
userName = "anonymous";
}
return userName;
}
public IResponse Handle(IRequest req) {
String userName = getUserName(req);
System.out.println("1. ---" + userName);
// 返回資訊帶上當前JVM所在機器的IP、程式號、時間
String message = String.format(RESPONSE_TEMPLETE,
userName,
getIpAddress(),
getPID(),
new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss" ).format(new Date()));
System.out.println("2. ---" + message);
// 響應內容也是JSON格式,所以先存入map,然後再序列化
MapString, Object> rlt = new HashMap>();
rlt.put("success", true);
rlt.put("message", message);
String rltStr = null;
try {
rltStr = mapper.writeValueAsString(rlt);
} catch (Exception e) {
e.printStackTrace();
}
Response res = new Response();
res.setContentType("application/json;charset=utf-8");
res.setBody(rltStr);
return res;
}
}
- pom.xml所在目錄下,新建資料夾m2,裡面增加maven的配置檔案settings.xml,該檔案是在FaaS開發過程中,製作映象時用到的(製作映象時會編譯構建java專案),強烈建議在裡面配置好您的maven私服,或者阿里雲映象,這樣製作映象時會快很多,我這裡已經配置了阿里雲映象,依然耗時四分多鐘(如下圖),所以如果您有nexus3私服一定要優先考慮:
- 至此,編碼工作已完成,可見這就是個普通maven工程,來試試能不能正常執行;
- 執行命令mvn clean package -U -DskipTests,成功後會在target目錄生成檔案java8maven-1.0-SNAPSHOT-jar-with-dependencies.jar;
- 執行上述jar檔案,命令是java -jar java8maven-1.0-SNAPSHOT-jar-with-dependencies.jar;
- 上述jar執行起來後會監聽8082埠的POST請求,我這裡用postman來試試,如下圖,可以收到後臺返回的最新資料:
-
- 後臺控制檯也會列印出預期的內容:
-
- 程式碼寫完了,接下來要考慮的如何製作Docker映象,即Dockerfile的編寫;
開發Dockerfile
- 前面的實戰中我們們已經體驗過,開發FaaS的時候會將程式碼編譯構建制作成映象,因此對應的Dockerfile也要準備好,下面是完整的Dockerfile內容,已經新增詳細的註釋,就不再贅述了:
# 用maven映象作為基礎映象,用於編譯構建java專案
FROM maven:3.6.3-openjdk-8 as builder
WORKDIR /home/app
# 將整個專案都複製到/home/app目錄下
COPY . /home/app/
# 進入pom.xml所在目錄執行構建命令,指定m2/settings.xml檔案作為配置檔案,
# 請在settings.xml中配置好私服,否則構建速度極慢
RUN cd function && mvn clean package -U -DskipTests --settings ./m2/settings.xml
# of-watchdog裡面有二進位制檔案watchdog,製作映象時要用到
FROM openfaas/of-watchdog:0.7.6 as watchdog
# openjdk映象是容器的執行環境
FROM openjdk:8-jre-slim as ship
# 為了安全起見,在生產環境執行容器時不要用指root帳號和群組
RUN addgroup --system app
&& adduser --system --ingroup app app
# 從of-watchdog映象中複製二進位制檔案fwatchdog,這是容器的啟動程式
COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog
# 賦予可執行許可權
RUN chmod +x /usr/bin/fwatchdog
WORKDIR /home/app
# 前面用maven編譯構建完畢後,這裡將構建結果複製到映象中
COPY --from=builder /home/app/function/target/java8maven-1.0-SNAPSHOT-jar-with-dependencies.jar ./java8maven-1.0-SNAPSHOT-jar-with-dependencies.jar
# 指定容器的執行帳號
user app
# 指定容器的工作目錄
WORKDIR /home/app/
# fwatchdog收到web請求後的轉發地址,java程式監聽的就是這個埠
ENV upstream_url=""
# 執行模式是http
ENV mode="http"
# 拉起業務程式的命令,這裡就是啟動java程式
ENV fprocess="java -jar java8maven-1.0-SNAPSHOT-jar-with-dependencies.jar"
# 容器對外暴露的埠,也就是fwatchdog程式監聽的埠
EXPOSE 8080
# 健康檢查
HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1
# 容器啟動命令,這裡是執行二進位制檔案fwatchdog
CMD ["fwatchdog"]
模板配置
- 現在材料已經準備完畢了,再整理一下準備提交到github上,就可以作為OpenFaaS模板使用了;
- 新建一個資料夾,名為simplejava8;
- simplejava8目錄下新建檔案template.yml,內容如下:
language: simplejava8
welcome_message: |
You have created a function using the java8 and maven template
- 將前面的Dockerfile檔案複製到simplejava8目錄下;
- 前面我們們建立的maven工程,最外層的資料夾名為java8maven,請將此資料夾改名為function,然後將整個資料夾都複製到simplejava8目錄下;
- 此刻的simplejava8目錄下應該是這些內容:
[root@hedy 002]# tree simplejava8
simplejava8
├── Dockerfile
├── function
│ ├── java8maven.iml
│ ├── m2
│ │ └── settings.xml
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── openfaas
│ │ │ └── function
│ │ │ └── Handler.java
│ │ └── resources
│ └── test
│ └── java
└── template.yml
11 directories, 6 files
- 將這些內容全部上傳到github上,我這裡路徑是github.com/zq2599/openfaas-templates/tree/master/template,這裡面已經有三個模板了,本次新增的如下圖紅框:
- 至此,模板製作完成,接下來驗證此模板是否可用;
驗證模板
- 接下來要做的,就是下圖右側的綠色部分:
- 登入一臺配好OpenFaaS客戶端的電腦,找個乾淨目錄執行以下命令,將github上所有模板下載下來:
faas template pull
- 控制檯響應如下,提示下載了三個模板,符合預期:
[root@hedy 07]# faas template pull
Fetch templates from repository: at
2021/03/07 08:44:29 Attempting to expand templates from
2021/03/07 08:44:32 Fetched 3 template(s) : [dockerfile java11extend simplejava8] from
- 用faas new --list檢視列表如下:
[root@hedy 07]# faas new --list
Languages available as templates:
- dockerfile
- java11extend
- simplejava8
- 看看template/simplejava8目錄下的內容,和前面上傳的一模一樣:
[root@hedy 07]# tree template/simplejava8/
template/simplejava8/
├── Dockerfile
├── function
│ ├── java8maven.iml
│ ├── m2
│ │ └── settings.xml
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── com
│ └── openfaas
│ └── function
│ └── Handler.java
└── template.yml
8 directories, 6 files
- 有了模板就可以建立函式了,執行以下命令建立名為faas-simplejava8demo的函式:
faas-cli new faas-simplejava8demo --lang simplejava8 -p bolingcavalry
- 控制檯提示如下,此時當前目錄下新增資料夾faas-simplejava8demo,這就是新建函式的程式碼目錄:
[root@hedy 07]# faas-cli new faas-simplejava8demo --lang simplejava8 -p bolingcavalry
Folder: faas-simplejava8demo created.
___ _____ ____
/ _ _ __ ___ _ __ | ___|_ _ __ _/ ___|
| | | | '_ / _ '_ | |_ / _` |/ _` ___
| |_| | |_) | __/ | | | _| (_| | (_| |___) |
___/| .__/ ___|_| |_|_| __,_|__,_|____/
|_|
Function created in folder: faas-simplejava8demo
Stack file written: faas-simplejava8demo.yml
Notes:
You have created a function using the java8 and maven template
[root@hedy 07]# ls
faas-simplejava8demo faas-simplejava8demo.yml template
- 資料夾faas-simplejava8demo的內容如下,現在妥了,用IDEA等IDE工具以maven工程形式匯入,然後根據業務需求修改這個工程即可:
[root@hedy 07]# tree faas-simplejava8demo
faas-simplejava8demo
├── java8maven.iml
├── m2
│ └── settings.xml
├── pom.xml
└── src
└── main
└── java
└── com
└── openfaas
└── function
└── Handler.java
7 directories, 4 files
- 現在可以開發業務了,這裡為了測試,新增了一行程式碼,如下圖紅框:
- 開始編譯構建吧,執行以下命令:
faas-cli build -f ./faas-simplejava8demo.yml
- 構建完成後將映象推送到映象倉庫,以便Kubernetes可以下載到此映象,我這裡用的是hub.docker.com,因為我的ID是bolingcavalry,所執行以下命令即可推送成功(要先執行docker login命令登入):
docker push bolingcavalry/faas-simplejava8demo:latest
- 執行以下命令部署函式到OpenFaaS:
faas-cli deploy -f faas-simplejava8demo.yml
- 控制檯響應如下,可見部署已經開始,並且給出了endpoint:
[root@hedy 07]# faas-cli deploy -f faas-simplejava8demo.yml
Deploying: faas-simplejava8demo.
WARNING! You are not using an encrypted connection to the gateway, consider using HTTPS.
Deployed. 202 Accepted.
URL: http://192.168.50.75:31112/function/faas-simplejava8demo.openfaas-fn
- 開啟web端,在頁面上可見新增的函式,驗證操作如下圖所示,可見入參的JSON內容可以被正常解析:
- 也可以在控制檯用curl命令測試:
[root@hedy 07]# curl
> -H "Content-Type: application/json"
> -X POST
> --data '{"name":"Jerry}'
> http://192.168.50.75:31112/function/faas-simplejava8demo
{"success":true,"foo":"bar","message":"Hello anonymous, response from [10.244.0.168], PID [14], 2021-03-07 03:32:15"}
清理
- 刪除函式的命令如下,依舊是faas-simplejava8demo.yml所在目錄:
faas-cli remove -f faas-simplejava8demo.yml
- 至此,自制的maven+jdk8的模板,從開發到驗證我們們已經全部走了一遍,相信您對OpenFaaS的理解也已經更加全面和深入了,本篇是為開發模板練手用的,實用價值不大,接下來的文章我們們要做個實用的模板:jdk8+maven+springboot
我是欣宸,期待與您一同暢遊Java世界…
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/855/viewspace-2807156/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 資料庫連線池-Druid資料庫連線池原始碼解析資料庫UI原始碼
- 《四 資料庫連線池原始碼》手寫資料庫連線池資料庫原始碼
- 資料庫連線池原理資料庫
- Flask資料庫連線池Flask資料庫
- python資料庫連線池Python資料庫
- 【MySQL】自定義資料庫連線池和開源資料庫連線池的使用MySql資料庫
- 聊聊資料庫連線池 Druid資料庫UI
- 資料庫連線池實現資料庫
- Javaweb-資料庫連線池JavaWeb資料庫
- 手寫資料庫連線池資料庫
- Python資料庫連線池DButilsPython資料庫
- MySql資料庫連線池專題MySql資料庫
- Java Druid資料庫連線池+SpringJDBCJavaUI資料庫SpringJDBC
- JavaWeb之事務&資料庫連線池JavaWeb資料庫
- mysql資料庫連線池配置教程MySql資料庫
- 資料庫連線池設計和實現(Java版本)資料庫Java
- 帶你進入資料庫連線池資料庫
- 資料庫連線池技術詳解資料庫
- Spring Boot整合Druid資料庫連線池Spring BootUI資料庫
- druid資料庫連線池的配置類UI資料庫
- Springboot 整合阿里資料庫連線池 druidSpring Boot阿里資料庫UI
- Druid資料庫連線池使用體驗UI資料庫
- 淺談JDBC和資料庫連線池JDBC資料庫
- 資料庫到底應該如何儲存密碼?資料庫密碼
- Spring框架中mysql資料庫連線池bean設定出錯Spring框架MySql資料庫Bean
- 資料庫連線池的實現及原理資料庫
- 資料庫連線池_druid基本使用&工具類資料庫UI
- django中的資料庫連線池實現Django資料庫
- golang兩種資料庫連線池實現Golang資料庫
- python資料庫連線池的正確用法Python資料庫
- springboot專案整合druid資料庫連線池Spring BootUI資料庫
- Druid資料庫連線池就這麼簡單UI資料庫
- 自定義帶監控的資料庫連線池資料庫
- 第 67 期 Go database/sql 資料庫連線池分析GoDatabaseSQL資料庫
- Go實戰準備工作---建立資料庫連線池Go資料庫
- 從原始碼分析DBCP資料庫連線池的原理原始碼資料庫
- ADO.NET入門教程之資料庫連線池資料庫
- Java技術分享:什麼是資料庫連線池?Java資料庫