玩转阿里云函数计算(一)----Java Http 触发器极速迁移传统 Spring 应用

简介: 前言 阿里云函数计算 Function Compute(FC),旨在帮助用户采用弹性伸缩、动态分配资源的方式,来执行业务函数。让用户无需购买部署服务器,无需考虑业务负载,就能快速搭建可处理高并发的后台服务。

前言

阿里云函数计算 Function Compute(FC),旨在帮助用户采用弹性伸缩、动态分配资源的方式,来执行业务函数。让用户无需购买部署服务器,无需考虑业务负载,就能快速搭建可处理高并发的后台服务。
函数计算平台针对 Java 语言推出的 Java HTTP 触发器功能,能够无缝迁移传统的 Java Web 应用。支持基于 Servlet 协议的 Web 框架所开发的应用,比如常用的 Spring、SpringBoot、Struts2等。
本文介绍如何使用 Java HTTP 触发器来快速迁移 Spring 提供的开源 Web 项目 GreenHouse

相关链接

开始迁移

Java HTTP 触发器使用示例代码

一、打包需要迁移的 Web 工程为 war 包

可以使用已编译好的 greenhouse.war,本步骤可直接跳过

  1. GreenHouse github 上下载 Web 工程源码。
  2. 在源码根目录执行 maven 打包命令 maven clean package -DskipTests

    注意 GreenHouse 使用的 JDK 版本是1.6,比较老。这里打包前需要修改下 pom.xml 为你使用的 JDK 版本。
    image

    打包成功后,在 target 目录下能看到 greenhouse-1.0.0.BUILD-SNAPSHOT.war 这个文件。最后为了方便将该 war 包重命名为 greenhouse.war

二、在函数计算平台创建 Java 函数

将要运行的应用 war 包可以和函数代码一起打包上传,也可以放在网络存储中比如 阿里云对象存储(OSS),或者任何其它的网络存储。以下示例将应用 war 包放在函数代码工程中和存储到 OSS 中两种方式。

方式一:应用 war 包放在函数代码工程

  1. 在本地创建 maven 工程,并创建一个 package 比如 com.aliyun.fc.example,在 package 中添加 Java 类 HelloWebLocal.java:

    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();
            }
        }
    }

    其中引用的 maven 库:

    <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>
  2. 将 greenhouse.war 拷贝到 Java 工程中,上述代码中 loadCodeFromLocalProject 方法参数为 greenhouse.war 文件在工程中的相对路径。比如放在 src/main/resources 目录,那就和上述代码保持一样,否则改成对应的相对路径。

方式二:应用 war 包存储到 OSS 中

如果将 war 包放在创建函数的 Java 工程中,会增加创建函数时上传的代码包大小。对于代码包大小函数计算有限制最大为 50M ,请参考函数计算使用限制。往往 Java 的 web 应用 war 包会比较大,因而更好的方式是将 war 包放在 OSS 中,然后通过初始化函数 initializer 来下载 war 包到执行环境中。同样可以调用 fc-java-common 库中的 loadCodeFromOSS 方法即可,该方法会将对应的 war 包下载到执行环境的临时磁盘目录 /tmp 中。

  1. 将 greenhouse.war 上传到 OSS 中
  2. 方式一创建 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 工程打包,并在函数计算平台创建服务和函数,这里需要注意的点:

  1. 需要将您创建的服务名和函数名,填充到上述 HelloWebLocal.java 中:

    private String userContextPath = "/2016-08-15/proxy/${YourServiceName}/${YourFunctionName}";
  2. 如果您使用的是 OSS 存储方式,需要填充 OSS 相关信息到 HelloWebOSS.java 中:

      private String ossEndPoint = "${YourOSSEndpoint}";
      private String bucket = "${YourOSSBucket}";
      private String userContextPath = "/2016-08-15/proxy/${YourServiceName}/${YourFunctionName}";
  3. 创建函数时除了需要设置函数入口外,还需要设置初始化入口指向上述代码的 initialize 函数。
  4. Web 应用使用的内存较多,请注意函数内存大小设置,比如我这里设置的是 512M

示例配置如下:
image

为函数创建 HTTP 触发器

关于怎么创建 HTTP 触发器请参考:HTTP 触发器

测试函数运行

在函数计算控制台执行 HTTP 触发器,看到如下返回:
image

可以看到成功返回了 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 触发的示例配置:
image

自定义域名路径前缀必须和代码中 userContextPath 保持一致,比如这里都是 /greenhouse。当然可以选择其它的路径名,也可以使用根路径,即路径配置为 /* 对应的 userContextPath 为 /
通过修改配置路径和对应的 userContextPath,可映射到不同的 HTTP 触发器函数,从而映射到不同的 web 应用中。这样就能同一个域名同时复用到不同的 web 应用。

浏览器中访问自定义域名:
shop

大功告成!

后语

函数计算是 serverless 服务,对于传统的 Web 应用支持目前还是存在一些瑕疵。

  1. 无状态性,每次函数执行都是无状态的。对于 Web 应用往往有很多状态需要保持,比如 session,用户需要在自己的 Web 应用中去处理。当然很多 web 框架已经提供了很方便的分布式 session 方案,只需简单的配置即可。
  2. 冷启动,新的执行环境中第一次请求 web 应用会比较耗时,这是由于执行环境需要启动 JVM 以及加载 web 应用,针对这个可通过定时预热的方式来解决。

但这些相对于 serverless 提供的弹性伸缩和按需付费的优点不值一提,个人认为 serverless 必定会取代传统物理机或虚拟机的服务器方式,从而让有限的资源得到更高效的利用。

相关实践学习
【玩转ComfyUI】基于函数计算一键部署AI生图平台ComfyUI
本次实验将带大家通过使用阿里云产品函数计算FC,快速使用ComfyUI实现更高质量的图像生成。
从 0 入门函数计算
在函数计算的架构中,开发者只需要编写业务代码,并监控业务运行情况就可以了。这将开发者从繁重的运维工作中解放出来,将精力投入到更有意义的开发任务上。
目录
相关文章
|
10月前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
1117 3
|
10月前
|
监控 Java 数据库
从零学 Dropwizard:手把手搭轻量 Java 微服务,告别 Spring 臃肿
Dropwizard 整合 Jetty、Jersey 等成熟组件,开箱即用,无需复杂配置。轻量高效,启动快,资源占用少,内置监控、健康检查与安全防护,搭配 Docker 部署便捷,是构建生产级 Java 微服务的极简利器。
1021 117
|
9月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
10月前
|
人工智能 Java API
构建基于Java的AI智能体:使用LangChain4j与Spring AI实现RAG应用
当大模型需要处理私有、实时的数据时,检索增强生成(RAG)技术成为了核心解决方案。本文深入探讨如何在Java生态中构建具备RAG能力的AI智能体。我们将介绍新兴的Spring AI项目与成熟的LangChain4j框架,详细演示如何从零开始构建一个能够查询私有知识库的智能问答系统。内容涵盖文档加载与分块、向量数据库集成、语义检索以及与大模型的最终合成,并提供完整的代码实现,为Java开发者开启构建复杂AI智能体的大门。
5537 58
|
9月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
790 8
|
11月前
|
前端开发 Java 开发者
Java新手指南:在Spring MVC中使用查询字符串与参数
通过结合实际的需求和业务逻辑,开发者可以灵活地利用这些机制,为用户提供更丰富而高效的Web应用体验。
259 15
|
12月前
|
JSON 前端开发 Java
Java新手指南:如何在Spring MVC中处理请求参数
处理Spring MVC中的请求参数是通过控制器方法中的注解来完成的。这些注解包括 `@RequestParam`, `@PathVariable`, `@ModelAttribute`, `@RequestBody`, `@RequestHeader`, `@Valid`, 和 `@RequestMapping`。使用这些注解可以轻松从HTTP请求中提取所需信息,例如URL参数、表单数据或者JSON请求体,并将其转换成Java对象以供进一步处理。
668 17
|
12月前
|
安全 Java 微服务
Java 最新技术和框架实操:涵盖 JDK 21 新特性与 Spring Security 6.x 安全框架搭建
本文系统整理了Java最新技术与主流框架实操内容,涵盖Java 17+新特性(如模式匹配、文本块、记录类)、Spring Boot 3微服务开发、响应式编程(WebFlux)、容器化部署(Docker+K8s)、测试与CI/CD实践,附完整代码示例和学习资源推荐,助你构建现代Java全栈开发能力。
1029 1
|
11月前
|
Cloud Native Java API
Java Spring框架技术栈选和最新版本及发展史详解(截至2025年8月)-优雅草卓伊凡
Java Spring框架技术栈选和最新版本及发展史详解(截至2025年8月)-优雅草卓伊凡
1750 0
|
12月前
|
前端开发 Java API
基于 Spring Boot 3 与 React 的 Java 学生信息管理系统从入门到精通实操指南
本项目基于Spring Boot 3与React 18构建学生信息管理系统,涵盖前后端开发、容器化部署及测试监控,提供完整实操指南与源码,助你掌握Java全栈开发技能。
618 0

相关产品

  • 函数计算