03.05 無服務架構(serverless)實踐


無服務架構(serverless)實踐

Serverless架構

無服務架構即FAAS是未來雲服務的發展趨勢,目前每個雲廠商都在用自己的方式推出了相關的產品。本文無意討論已經被廣泛認知的架構理論,而是從具體工程實踐過程來敘述如何使用FAAS模式來實現無服務架構。本文涉及的工程實踐是基於騰訊雲提供的serverless平臺(函數服務),雖然騰訊雲提供了相關的文檔和示例代碼,但是經過研究發現還是存在一些不明確的地方,特別是對JAVA語言的實現說明就更少了,我經過幾天的摸索,終於完成了全流程,在此記錄一下,也給喜歡探索新技術的同學提供一些幫助。

開通“雲函數”

登錄騰訊雲的“控制檯”,在“雲產品”列表中的“serverless”下面選擇“雲函數”進入到雲函數管理頁。這裡可以總覽運行在serverless平臺上的雲函數。

無服務架構(serverless)實踐

雲函數dashboard

點擊“函數服務”就可以管理你的雲函數了。

無服務架構(serverless)實踐

函數服務管理自己的雲函數

可以設置命名空間來對你的函數服務進行分組管理。

新建“函數服務”

騰訊雲目前支持Python、Node.js、PHP、GoLang、JAVA語言的運行環境,因為雲函數的運行模式所以建議採用Python、Node.js、PHP等腳本語言實現,編譯語言目前的運行性能還不是很高,所以儘量不要使用JAVA語言,但是考慮到JAVA語言的生態,我想serverless未來也會加強對JAVA語言的支持力度的,所以騰訊雲對JAVA的“模板函數”也只提供了一個最簡單的HelloWorld,也是本文要詳述的原因。

填寫好“函數名稱”後,選擇“運行環境”為“Java8”,再選擇“空白函數模板”。

無服務架構(serverless)實踐

新建函數服務

配置“函數服務”

在“函數配置”裡面設置“執行方法”,java的執行方法必須包括包名稱、類名稱、方法名稱。

其中包名稱不支持多級命名,即package的名字不能是xxx.xxx的方式,只能有一級。

JAVA是編譯方式運行的,所以不提供雲編譯環境,只能離線編譯,將編譯後的jar文件或者zip文件上傳到雲平臺。

無服務架構(serverless)實踐

函數配置

這些函數服務都可以在今後再次進行編譯,相關的代碼包也可以隨時上傳。

開發JAVA版的“函數”

  • 工程結構
無服務架構(serverless)實踐

函數服務的工程結構

以maven來組織的java項目和以為的項目結構沒有什麼大的區別,唯一要注意的是package名稱不能為多級。

  • 依賴包

考慮到函數服務要通過API網關暴露出去,就需要引入騰訊提供的scf事件包,還要使用數據庫連接及Mysql數據庫就要引入HikariCP,還要使用fastjson來實現JSON的相關功能,還要使用lombok的Bean註解功能。

<code>    <dependencies>
\t
<dependency>
<groupid>com.tencentcloudapi/<groupid>
<artifactid>scf-java-events/<artifactid>
<version>0.0.1/<version>
/<dependency>

<dependency>
<groupid>com.alibaba/<groupid>
<artifactid>fastjson/<artifactid>
<version>1.2.30/<version>
/<dependency>

<dependency>
<groupid>org.projectlombok/<groupid>
<artifactid>lombok/<artifactid>
<version>1.18.2/<version>
/<dependency>
\t\t\t\t
<dependency>
<groupid>com.zaxxer/<groupid>
<artifactid>HikariCP/<artifactid>
<version>3.2.0/<version>
/<dependency>
<dependency>
<groupid>mysql/<groupid>
<artifactid>mysql-connector-java/<artifactid>
<version>8.0.11/<version>
/<dependency>
/<dependencies>/<code>
  • package命名規則

沒有太明白serverless為什麼對java的支持不允許多級package名稱,現在只能使用以及package名稱,否則配置“函數服務”報錯。

<code>package serverless;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.qcloud.scf.runtime.Context;
import com.qcloud.services.scf.runtime.events.APIGatewayProxyRequestEvent;
import com.qcloud.services.scf.runtime.events.APIGatewayProxyResponseEvent;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import serverless.pojo.UserInfo;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;/<code>
  • 入參、出參

Serverless都是以函數的方式提供服務的,所以函數是“函數服務”的唯一運行單元,對應到JAVA中就是class裡邊的public型方法,既然是函數就存在輸入、輸出,也就是入參、出參。

入參包括:JAVA基本類型、POJO對象、JSON對象;

出參包括:JAVA基本類型、JSON String對象;

<code>    public Integer saveHandler(UserInfo userInfo){
System.out.println("開始保存用戶信息...");
int result=0;
try (Connection conn = dataSource.getConnection()) {
String sql="insert into UserInfo(userId,mobileNumber,firstName,secondName,password) value (?,?,?,?,?)";
PreparedStatement pstmt= conn.prepareStatement(sql);
pstmt.setString(1, UUID.randomUUID().toString());
pstmt.setString(2,userInfo.getMobileNumber());
pstmt.setString(3,userInfo.getFirstName());
pstmt.setString(4,userInfo.getSecondName());
pstmt.setString(5,userInfo.getPassword());
result=pstmt.executeUpdate();

System.out.println("保存用戶信息成功!");
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}/<code>
<code>package serverless.pojo;

import lombok.Data;

@Data
public class UserInfo {
private String userId;
private String firstName;
private String secondName;
private String mobileNumber;
private String password;
}/<code>
  • 連接數據庫

數據庫連接的參數可以通過“函數服務”的環境變量來配置,不用硬編碼到程序中。

無服務架構(serverless)實踐

MySQL數據庫連接的參數配置到環境變量中

數據庫連接的代碼放到構造函數中,這樣在函數服務創建的時候就建立好相關的對象。

<code>    public Http() {
//配置數據庫
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://" + System.getenv("DB_HOST") + ":"+ System.getenv("DB_PORT") + "/" + System.getenv("DB_DATABASE"));
config.setUsername(System.getenv("DB_USER"));
config.setPassword(System.getenv("DB_PASSWORD"));
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setMaximumPoolSize(1);
config.addDataSourceProperty("useUnicode",true);
config.addDataSourceProperty("characterEncoding","utf8");
dataSource = new HikariDataSource(config);
}/<code>
  • 在線測試

開發完相關的功能後就可以在線測試了,如果入參是JSON對象,則選擇“HelloWord事件模板”,通過點擊該模板名稱右邊的“眼睛”圖標可以對入參進行修改。

無服務架構(serverless)實踐

JSON格式的入參配置

其他類型的入參可以從模板選擇進行配置,也可以自定義模板。點擊“測試”按鈕就可以看到當前函數的運行結果了。

無服務架構(serverless)實踐

在線測試運行結果

通過API網關訪問“函數服務”

經過上面幾個步驟說明雲函數已經開發部署完成,那麼怎麼提供給外部使用呢?雲函數通過觸發方式提供若各種服務支持,本文只介紹“API網關觸發器”,配置好後就可以和使用普通RESTFul接口的方式來調用雲函數了。

無服務架構(serverless)實踐

添加函數觸發器

每個函數可以添加多個觸發器

無服務架構(serverless)實踐

配置好的API網關觸發器

“啟用集成響應”是要求函數返回的參數必須符合serverless規範要求的JSON格式,官站提供的例子有問題,必須將APIGatewayProxyResponseEvent轉換為JSON String才能夠正常解析。

<code>    public String selectHandler(APIGatewayProxyRequestEvent requestEvent, Context context) {
System.out.println("開始查詢用戶信息...");
List<userinfo> userInfoList=new ArrayList<>();
try (Connection conn = dataSource.getConnection()) {
PreparedStatement ps = conn.prepareStatement("SELECT * FROM UserInfo");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
UserInfo userInfo=new UserInfo();
userInfo.setUserId(rs.getString("userId"));
userInfo.setFirstName(rs.getString("firstName"));
userInfo.setSecondName(rs.getString("secondName"));
userInfo.setMobileNumber(rs.getString("mobileNumber"));
userInfo.setPassword(rs.getString("password"));
userInfoList.add(userInfo);
}
System.out.println("查詢用戶信息結束!");
} catch (SQLException e) {
e.printStackTrace();
}

//配置返回給API網關的格式化數據
APIGatewayProxyResponseEvent apiGatewayProxyResponseEvent = new APIGatewayProxyResponseEvent();

apiGatewayProxyResponseEvent.setIsBase64Encoded(false);
apiGatewayProxyResponseEvent.setStatusCode(200);

Map<string> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
headers.put("Access-Control-Allow-Origin", "*");
apiGatewayProxyResponseEvent.setHeaders(headers);

apiGatewayProxyResponseEvent.setBody(JSONArray.toJSONString(userInfoList));

return JSONObject.toJSONString(apiGatewayProxyResponseEvent);
}/<string>/<userinfo>/<code>


分享到:


相關文章: