[函数计算] Serverless 微服务实践-移动应用包分发服务

简介: 移动应用的打包和分发呈现明显的峰谷效用,用户常常需要短时间内准备大量资源保障分发的实时性,完成分发后又需要及时释放资源,降低成本。本次分享将介绍如何通过函数计算构建 Serverless 架构的包分发服务,在开发运维效率,性能和成本间取得良好的平衡。

移动应用的打包和分发呈现明显的峰谷效用,用户常常需要短时间内准备大量资源保障分发的实时性,完成分发后又需要及时释放资源,降低成本。本次分享将介绍如何通过函数计算构建 Serverless 架构的包分发服务,在开发运维效率,性能和成本间取得良好的平衡。

APK分包简介

image

一个应用包发布后,通常会分发给各个应用市场去推广,终端用户可能会通过不同的应用市场来下载安装应用。为了追踪应用在各个市场的下载安装情况,需要为每个应用市场的apk包打上渠道标记,用户下载安装后,应用执行时,可以获取到这个渠道信息,做一些个性化的服务。

分包的流程分为3步:

  1. 下载原始的apk包
  2. 写入渠道信息,生成新的apk包
  3. 上传新的apk包

image

应用的渠道可能会有很多,甚至上千个。所以一个应用包要生成上千个新的应用包。在分包过程中,下载/修改/上传是一个比较消耗资源的任务,需要消耗大量的计算/网络资源。并且分包任务只在应用发布新版本时才会发生,需要在尽可能短的时间内完成。

针对这种有明显波峰波谷的场景,非常适合使用函数计算来完成。

实验步骤

在这个实验中,我们会使用一个示例的apk包,可以从这里下载 qq-v2.apk

写入渠道信息的方式,我们使用美团的开源工具 walle

1. 准备apk包

下载 qq-v2.apk ,上传到自己的oss bucket中。

2. 编写函数

接下来我们编写函数,对这个apk包进行分包操作。函数的主要流程是:

  1. 从OSS bucket中下载原始apk包到/tmp/目录
  2. 调用 walle-cli 对这个apk包进行处理,写入渠道信息,示例中我们的渠道信息为"aliyun-fc"
  3. 将生成的apk包重新上传到OSS

我们使用 fun 工具来初始化一个java8的函数:

$ fun init helloworld-java8
Start rendering template...
+ /private/tmp/04-25
+ /private/tmp/04-25/pom.xml
+ /private/tmp/04-25/src
+ /private/tmp/04-25/src/main
+ /private/tmp/04-25/src/main/java
+ /private/tmp/04-25/src/main/java/example
+ /private/tmp/04-25/src/main/java/example/App.java
+ /private/tmp/04-25/src/test
+ /private/tmp/04-25/src/test/java
+ /private/tmp/04-25/src/test/java/example
+ /private/tmp/04-25/src/test/java/example/AppTest.java
+ /private/tmp/04-25/template.yml
finish rendering template.

在当前目录创建一个 .env 文件,内容如下,其中花括号的内容替换成自己的:

ACCOUNT_ID={阿里云uid}
REGION=cn-shanghai
ACCESS_KEY_ID={access key id}
ACCESS_KEY_SECRET={access key secret}

先部署函数,然后尝试运行一下:

mvn package
fun deploy

在控制台运行函数:

image

接下来,我们修改函数,加入分包逻辑:

  1. 修改template.yml: 超时时间改成60,内存改成1024,CodeUri: "./target/.", 设置Role
  2. 修改pom.xml: 增加OSS的SDK
  3. 下载 walle-cli-all.jar ,放到 ./target/ 目录下
  4. 修改App.java: 加入分包逻辑

template.yml:

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  demo:
    Type: 'Aliyun::Serverless::Service'
    Properties:
      Description: 'helloworld'
      Role: acs:ram::1237050315505682:role/fc-service-role
    demo:
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: example.App::handleRequest
        Runtime: java8
        Timeout: 60
        MemorySize: 1024
        CodeUri: './target/'

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>example</groupId>
  <artifactId>demo</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>demo</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.aliyun.fc.runtime</groupId>
      <artifactId>fc-java-core</artifactId>
      <version>1.0.0</version>
    </dependency>
    <dependency>
      <groupId>com.aliyun.oss</groupId>
      <artifactId>aliyun-sdk-oss</artifactId>
      <version>2.8.3</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
          <archive>
            <manifest>
            </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
</project>

App.java:

package example;

import java.io.*;

import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.StreamRequestHandler;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.GetObjectRequest;

/**
 * Hello world!
 *
 */
public class App implements StreamRequestHandler
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
    }

    @Override
    public void handleRequest(
            InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
        OSSClient client = new OSSClient(
                "http://oss-cn-shanghai.aliyuncs.com",
                context.getExecutionCredentials().getAccessKeyId(),
                context.getExecutionCredentials().getAccessKeySecret(),
                context.getExecutionCredentials().getSecurityToken());

        String bucketName = "rockuw-sh";
        String objectName = "qq-v2.apk";
        String outObjectName = "qq-v2-signed.apk";
        String inputApk = "/tmp/input.apk";
        String outputApk = "/tmp/output.apk";

        // 1. download original apk
        client.getObject(new GetObjectRequest(bucketName, objectName), new File(inputApk));

        // 2. adding channel info
        String cmd = "java -jar /code/walle-cli-all.jar put -c aliyun-fc";
        cmd += " " + inputApk;
        cmd += " " + outputApk;

        context.getLogger().info("cmd: " + cmd);

        ProcessBuilder processBuilder = new ProcessBuilder();
        processBuilder.command("bash", "-c", cmd);

        try {
            Process process = processBuilder.start();
            StringBuilder output = new StringBuilder();
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));

            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line + "\n");
            }

            int exitVal = process.waitFor();
            if (exitVal == 0) {
                context.getLogger().info("Success!");
                outputStream.write("Success".getBytes());
            } else {
                //abnormal...
                context.getLogger().error("Failed!");
                context.getLogger().error("status: " + exitVal);
                outputStream.write("Failed".getBytes());
            }
            System.out.println(output);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 3. upload new apk
        client.putObject(bucketName, outObjectName, new File(outputApk));

        client.shutdown();
    }
}

3. 运行函数

在FC控制台执行函数:

image

4. 查看结果

执行成功后,可以看到,OSS bucket中已经生成了新的apk文件:

image

把文件下载下来查看,确实已经有了渠道信息:

image

总结

经过简单的几个步骤,我们就实现了一个apk分包的服务,仅仅只需要不到80行代码。更重要的是这个服务是具有弹性伸缩和高可用能力的,即使有上千个包要分发也可以轻松应对。

相关实践学习
函数计算部署PuLID for FLUX人像写真实现智能换颜效果
只需一张图片,生成程序员专属写真!本次实验在函数计算中内置PuLID for FLUX,您可以通过函数计算+Serverless应用中心一键部署Flux模型,快速体验超写实图像生成的魅力。
从 0 入门函数计算
在函数计算的架构中,开发者只需要编写业务代码,并监控业务运行情况就可以了。这将开发者从繁重的运维工作中解放出来,将精力投入到更有意义的开发任务上。
目录
相关文章
|
7月前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
2230 10
|
人工智能 开发框架 安全
Serverless MCP 运行时业界首发,函数计算让 AI 应用最后一公里提速
作为云上托管 MCP 服务的最佳运行时,函数计算 FC 为阿里云百炼 MCP 提供弹性调用能力,用户只需提交 npx 命令即可“零改造”将开源 MCP Server 部署到云上,函数计算 FC 会准备好计算资源,并以弹性、可靠的方式运行 MCP 服务,按实际调用时长和次数计费,欢迎你在阿里云百炼和函数计算 FC 上体验 MCP 服务。
993 31
|
10月前
|
存储 编解码 Serverless
Serverless架构下的OSS应用:函数计算FC自动处理图片/视频转码(演示水印添加+缩略图生成流水线)
本文介绍基于阿里云函数计算(FC)和对象存储(OSS)构建Serverless媒体处理流水线,解决传统方案资源利用率低、运维复杂、成本高等问题。通过事件驱动机制实现图片水印添加、多规格缩略图生成及视频转码优化,支持毫秒级弹性伸缩与精确计费,提升处理效率并降低成本,适用于高并发媒体处理场景。
1165 0
|
Cloud Native Serverless 流计算
云原生时代的应用架构演进:从微服务到 Serverless 的阿里云实践
云原生技术正重塑企业数字化转型路径。阿里云作为亚太领先云服务商,提供完整云原生产品矩阵:容器服务ACK优化启动速度与镜像分发效率;MSE微服务引擎保障高可用性;ASM服务网格降低资源消耗;函数计算FC突破冷启动瓶颈;SAE重新定义PaaS边界;PolarDB数据库实现存储计算分离;DataWorks简化数据湖构建;Flink实时计算助力风控系统。这些技术已在多行业落地,推动效率提升与商业模式创新,助力企业在数字化浪潮中占据先机。
644 12
|
人工智能 开发框架 运维
Serverless MCP 运行时业界首发,函数计算让 AI 应用最后一公里提速
Serverless MCP 运行时业界首发,函数计算支持阿里云百炼 MCP 服务!阿里云百炼发布业界首个全生命周期 MCP 服务,无需用户管理资源、开发部署、工程运维等工作,5 分钟即可快速搭建一个连接 MCP 服务的 Agent(智能体)。作为云上托管 MCP 服务的最佳运行时,函数计算 FC 为阿里云百炼 MCP 提供弹性调用能力。
 Serverless MCP 运行时业界首发,函数计算让 AI 应用最后一公里提速
|
监控 Kubernetes Cloud Native
基于阿里云容器服务Kubernetes版(ACK)的微服务架构设计与实践
本文介绍了如何基于阿里云容器服务Kubernetes版(ACK)设计和实现微服务架构。首先概述了微服务架构的优势与挑战,如模块化、可扩展性及技术多样性。接着详细描述了ACK的核心功能,包括集群管理、应用管理、网络与安全、监控与日志等。在设计基于ACK的微服务架构时,需考虑服务拆分、通信、发现与负载均衡、配置管理、监控与日志以及CI/CD等方面。通过一个电商应用案例,展示了用户服务、商品服务、订单服务和支付服务的具体部署步骤。最后总结了ACK为微服务架构提供的强大支持,帮助应对各种挑战,构建高效可靠的云原生应用。
|
搜索推荐 NoSQL Java
微服务架构设计与实践:用Spring Cloud实现抖音的推荐系统
本文基于Spring Cloud实现了一个简化的抖音推荐系统,涵盖用户行为管理、视频资源管理、个性化推荐和实时数据处理四大核心功能。通过Eureka进行服务注册与发现,使用Feign实现服务间调用,并借助Redis缓存用户画像,Kafka传递用户行为数据。文章详细介绍了项目搭建、服务创建及配置过程,包括用户服务、视频服务、推荐服务和数据处理服务的开发步骤。最后,通过业务测试验证了系统的功能,并引入Resilience4j实现服务降级,确保系统在部分服务故障时仍能正常运行。此示例旨在帮助读者理解微服务架构的设计思路与实践方法。
905 17
|
消息中间件 运维 安全
后端开发中的微服务架构实践与挑战####
在数字化转型的浪潮中,微服务架构凭借其高度的灵活性和可扩展性,成为众多企业重构后端系统的首选方案。本文将深入探讨微服务的核心概念、设计原则、关键技术选型及在实际项目实施过程中面临的挑战与解决方案,旨在为开发者提供一套实用的微服务架构落地指南。我们将从理论框架出发,逐步深入至技术细节,最终通过案例分析,揭示如何在复杂业务场景下有效应用微服务,提升系统的整体性能与稳定性。 ####
325 32
|
监控 Cloud Native Java
基于阿里云容器服务(ACK)的微服务架构设计与实践
本文介绍如何利用阿里云容器服务Kubernetes版(ACK)构建高可用、可扩展的微服务架构。通过电商平台案例,展示基于Java(Spring Boot)、Docker、Nacos等技术的开发、容器化、部署流程,涵盖服务注册、API网关、监控日志及性能优化实践,帮助企业实现云原生转型。
|
消息中间件 运维 API
后端开发中的微服务架构实践####
本文深入探讨了微服务架构在后端开发中的应用,从其定义、优势到实际案例分析,全面解析了如何有效实施微服务以提升系统的可维护性、扩展性和灵活性。不同于传统摘要的概述性质,本摘要旨在激发读者对微服务架构深度探索的兴趣,通过提出问题而非直接给出答案的方式,引导读者深入
282 1

相关产品

  • 函数计算