前言
阿裡雲函數計算Function Compute(FC),旨在幫助使用者采用彈性伸縮、動态配置設定資源的方式,來執行業務函數。讓使用者無需購買部署伺服器,無需考慮業務負載,就能快速搭建可處理高并發的背景服務。
函數計算平台針對 Java 語言推出的 Java HTTP 觸發器功能,能夠無縫遷移傳統的 Java Web 應用。支援基于 Servlet 協定的 Web 架構所開發的應用,比如常用的 Spring、SpringBoot、Struts2等。
本文介紹如何使用 Java HTTP 觸發器來快速遷移 Spring 提供的開源 Web 項目
GreenHouse。
相關連結
開始遷移
Java HTTP 觸發器使用示例代碼一、打包需要遷移的 Web 工程為 war 包
可以使用已編譯好的
greenhouse.war,本步驟可直接跳過
- GreenHouse github 上下載下傳 Web 工程源碼。
- 在源碼根目錄執行 maven 打包指令
maven clean package -DskipTests
注意 GreenHouse 使用的 JDK 版本是1.6,比較老。這裡打包前需要修改下 pom.xml 為你使用的 JDK 版本。![]()
玩轉阿裡雲函數計算(一)----Java Http 觸發器極速遷移傳統 Spring 應用
打包成功後,在 target 目錄下能看到
greenhouse-1.0.0.BUILD-SNAPSHOT.war
這個檔案。最後為了友善将該 war 包重命名為
greenhouse.war
二、在函數計算平台建立 Java 函數
将要運作的應用 war 包可以和函數代碼一起打包上傳,也可以放在網絡存儲中比如 阿裡雲對象存儲(OSS),或者任何其它的網絡存儲。以下示例将應用 war 包放在函數代碼工程中和存儲到 OSS 中兩種方式。
方式一:應用 war 包放在函數代碼工程
- 在本地建立 maven 工程,并建立一個 package 比如
,在 package 中添加 Java 類 HelloWebLocal.java:com.aliyun.fc.example
其中引用的 maven 庫:public class HelloWebLocal implements FunctionInitializer, HttpRequestHandler { private FcAppLoader fcAppLoader = new FcAppLoader(); private String key = "greenhouse.war"; private String userContextPath = "/2016-08-15/proxy/${YourServiceName}/${YourFunctionName}"; @Override public void initialize(Context context) throws IOException { FunctionComputeLogger fcLogger = context.getLogger(); fcAppLoader.setFCContext(context); // Set war file path fcAppLoader.loadCodeFromLocalProject(key); // Init webapp from code long timeBegin = System.currentTimeMillis(); fcLogger.info("Loading webapp: " + key); boolean initSuccess = fcAppLoader.initApp(userContextPath, HelloWebOSS.class.getClassLoader()); if(! initSuccess) { throw new IOException("Init web app failed"); } fcLogger.info("Loaded webapp, elapsed: " + (System.currentTimeMillis() - timeBegin) + "ms"); } @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response, Context context) throws IOException, ServletException { try { fcAppLoader.forward(request, response); } catch (Exception e) { e.printStackTrace(); } } }
<dependency> <groupId>com.aliyun.fc.runtime</groupId> <artifactId>fc-java-core</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>com.aliyun.fc.runtime</groupId> <artifactId>fc-java-common</artifactId> <version>1.0.0</version> </dependency>
- 将 greenhouse.war 拷貝到 Java 工程中,上述代碼中
方法參數為 greenhouse.war 檔案在工程中的相對路徑。比如放在loadCodeFromLocalProject
目錄,那就和上述代碼保持一樣,否則改成對應的相對路徑。src/main/resources
方式二:應用 war 包存儲到 OSS 中
如果将 war 包放在建立函數的 Java 工程中,會增加建立函數時上傳的代碼包大小。對于代碼包大小函數計算有限制最大為 50M ,請參考
函數計算使用限制。往往 Java 的 web 應用 war 包會比較大,因而更好的方式是将 war 包放在 OSS 中,然後通過初始化函數 initializer 來下載下傳 war 包到執行環境中。同樣可以調用
fc-java-common
庫中的
loadCodeFromOSS
方法即可,該方法會将對應的 war 包下載下傳到執行環境的臨時磁盤目錄
/tmp
中。
- 将 greenhouse.war 上傳到 OSS 中
- 同
建立 maven 工程以及 Java package,并建立 HelloWebOSS.java:方式一
public class HelloWebOSS implements FunctionInitializer, HttpRequestHandler { private FcAppLoader fcAppLoader = new FcAppLoader(); private String ossEndPoint = "${YourOSSEndpoint}"; private String bucket = "${YourOSSBucket}"; private String key = "greenhouse.war"; private String userContextPath = "/2016-08-15/proxy/${YourServiceName}/${YourFunctionName}"; @Override public void initialize(Context context) throws IOException { FunctionComputeLogger fcLogger = context.getLogger(); fcAppLoader.setFCContext(context); // Load code from OSS fcLogger.info("Begin load code: " + key); boolean codeSuccess = fcAppLoader.loadCodeFromOSS(ossEndPoint, bucket, key); if (! codeSuccess) { throw new IOException("Download code failed"); } fcLogger.info("End load code"); // Init webapp from code long timeBegin = System.currentTimeMillis(); fcLogger.info("Begin load webapp"); boolean initSuccess = fcAppLoader.initApp(userContextPath, HelloWebOSS.class.getClassLoader()); if(! initSuccess) { throw new IOException("Init web app failed"); } fcLogger.info("End load webapp, elapsed: " + (System.currentTimeMillis() - timeBegin) + "ms"); } @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response, Context context) throws IOException, ServletException { try { fcAppLoader.forward(request, response); } catch (Exception e) { e.printStackTrace(); } } }
函數計算平台建立函數
關于怎麼在函數計算平台建立函數請參考:
Hello World 示例關于怎麼使用 Java 語言請參考:
Java 運作環境将上述的 maven 工程打包,并在函數計算平台建立服務和函數,這裡需要注意的點:
- 需要将您建立的服務名和函數名,填充到上述
中:HelloWebLocal.java
private String userContextPath = "/2016-08-15/proxy/${YourServiceName}/${YourFunctionName}";
- 如果您使用的是 OSS 存儲方式,需要填充 OSS 相關資訊到
HelloWebOSS.java
private String ossEndPoint = "${YourOSSEndpoint}"; private String bucket = "${YourOSSBucket}"; private String userContextPath = "/2016-08-15/proxy/${YourServiceName}/${YourFunctionName}";
- 建立函數時除了需要設定函數入口外,還需要設定初始化入口指向上述代碼的
函數。initialize
- Web 應用使用的記憶體較多,請注意函數記憶體大小設定,比如我這裡設定的是 512M
示例配置如下:
為函數建立 HTTP 觸發器
關于怎麼建立 HTTP 觸發器請參考:
HTTP 觸發器測試函數運作
在函數計算控制台執行 HTTP 觸發器,看到如下傳回:
可以看到成功傳回了 greenhouse 的 web 頁面。
但是如果在浏覽器中執行 HTTP 觸發器的請求位址,web 頁面将會已檔案的形式下載下傳。這是因為函數計算為了安全強制設定了請求傳回的 header 為
Content-Disposition: attachment
,是以傳回結果會以附件形式下載下傳。
為 HTTP 觸發器綁定自定義域名
為了解決這個問題,需要使用者申請自定義域名,并綁定域名解析到函數計算中,請參考
函數計算綁定自定義域名使用自定義域名通路,需要将最初的 Java 代碼中
userContextPath
改為自定義域名的 contextPath 比如
/
/greenhouse
等,這樣才能保證頁面中的超連結是相對于目前請求的域名:
// Not use custom domain
//private String userContextPath = "/2016-08-15/proxy/${YourServiceName}/${YourFunctionName}";
// Use custom domain
private String userContextPath = "/greenhouse";
自定義域名綁定到 HTTP 觸發的示例配置:
自定義域名路徑字首必須和代碼中 userContextPath 保持一緻,比如這裡都是。當然可以選擇其它的路徑名,也可以使用根路徑,即路徑配置為 /* 對應的 userContextPath 為
/greenhouse
通過修改配置路徑和對應的 userContextPath,可映射到不同的 HTTP 觸發器函數,進而映射到不同的 web 應用中。這樣就能同一個域名同時複用到不同的 web 應用。
/
浏覽器中通路自定義域名:
大功告成!
後語
函數計算是 serverless 服務,對于傳統的 Web 應用支援目前還是存在一些瑕疵。
- 無狀态性 ,每次函數執行都是無狀态的。對于 Web 應用往往有很多狀态需要保持,比如 session,使用者需要在自己的 Web 應用中去處理。當然很多 web 架構已經提供了很友善的分布式 session 方案,隻需簡單的配置即可,可以參考 Spring Session Redis
- 冷啟動 ,新的執行環境中第一次請求 web 應用會比較耗時,這是由于執行環境需要啟動 JVM 以及加載 web 應用,針對這個可通過定時預熱的方式來解決。
但這些相對于 serverless 提供的彈性伸縮和按需付費的優點不值一提,個人認為 serverless 必定會取代傳統實體機或虛拟機的伺服器方式,進而讓有限的資源得到更高效的利用。