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

本文涉及的产品
函数计算FC,每月15万CU 3个月
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
简介: 移动应用的打包和分发呈现明显的峰谷效用,用户常常需要短时间内准备大量资源保障分发的实时性,完成分发后又需要及时释放资源,降低成本。本次分享将介绍如何通过函数计算构建 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行代码。更重要的是这个服务是具有弹性伸缩和高可用能力的,即使有上千个包要分发也可以轻松应对。

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
1月前
|
Cloud Native Java API
聊聊从单体到微服务架构服务演化过程
本文介绍了从单体应用到微服务再到云原生架构的演进过程。单体应用虽易于搭建和部署,但难以局部更新;面向服务架构(SOA)通过模块化和服务总线提升了组件复用性和分布式部署能力;微服务则进一步实现了服务的独立开发与部署,提高了灵活性;云原生架构则利用容器化、微服务和自动化工具,实现了应用在动态环境中的弹性扩展与高效管理。这一演进体现了软件架构向着更灵活、更高效的方向发展。
|
2月前
|
监控 负载均衡 安全
微服务(五)-服务网关zuul(一)
微服务(五)-服务网关zuul(一)
|
24天前
|
Kubernetes 负载均衡 Docker
构建高效后端服务:微服务架构的探索与实践
【10月更文挑战第20天】 在数字化时代,后端服务的构建对于任何在线业务的成功至关重要。本文将深入探讨微服务架构的概念、优势以及如何在实际项目中有效实施。我们将从微服务的基本理念出发,逐步解析其在提高系统可维护性、扩展性和敏捷性方面的作用。通过实际案例分析,揭示微服务架构在不同场景下的应用策略和最佳实践。无论你是后端开发新手还是经验丰富的工程师,本文都将为你提供宝贵的见解和实用的指导。
|
23天前
|
监控 API 持续交付
构建高效后端服务:微服务架构的深度探索
【10月更文挑战第20天】 在数字化时代,后端服务的构建对于支撑复杂的业务逻辑和海量数据处理至关重要。本文深入探讨了微服务架构的核心理念、实施策略以及面临的挑战,旨在为开发者提供一套构建高效、可扩展后端服务的方法论。通过案例分析,揭示微服务如何帮助企业应对快速变化的业务需求,同时保持系统的稳定性和灵活性。
46 9
|
25天前
|
监控 安全 Java
构建高效后端服务:微服务架构深度解析与最佳实践###
【10月更文挑战第19天】 在数字化转型加速的今天,企业对后端服务的响应速度、可扩展性和灵活性提出了更高要求。本文探讨了微服务架构作为解决方案,通过分析传统单体架构面临的挑战,深入剖析微服务的核心优势、关键组件及设计原则。我们将从实际案例入手,揭示成功实施微服务的策略与常见陷阱,为开发者和企业提供可操作的指导建议。本文目的是帮助读者理解如何利用微服务架构提升后端服务的整体效能,实现业务快速迭代与创新。 ###
60 2
|
30天前
|
消息中间件 Kafka 数据库
微服务架构中,如何确保服务之间的数据一致性?
微服务架构中,如何确保服务之间的数据一致性?
|
2月前
|
运维 Kubernetes 前端开发
拥抱Knative, 合思加速Serverless化演进实践
合思信息基于阿里云容器服务Knative, 实现Serverless化演进的最佳实践。
拥抱Knative, 合思加速Serverless化演进实践
|
27天前
|
运维 Kubernetes 开发者
构建高效后端服务:微服务架构与容器化技术的结合
【10月更文挑战第18天】 在数字化转型的浪潮中,企业对后端服务的要求日益提高,追求更高的效率、更强的可伸缩性和更易于维护的系统。本文将探讨微服务架构与容器化技术如何结合,以构建一个既灵活又高效的后端服务体系。通过分析当前后端服务面临的挑战,介绍微服务和容器化的基本概念,以及它们如何相互配合来优化后端服务的性能和管理。本文旨在为开发者提供一种实现后端服务现代化的方法,从而帮助企业在竞争激烈的市场中脱颖而出。
25 0
|
2月前
|
消息中间件 Kafka 数据库
微服务架构中,如何确保服务之间的数据一致性
微服务架构中,如何确保服务之间的数据一致性
|
2月前
|
消息中间件 缓存 NoSQL
构建高效后端服务:微服务架构的深度实践
本文旨在探讨如何通过采用微服务架构来构建高效的后端服务。我们将深入分析微服务的基本概念、设计原则以及在实际项目中的应用案例,揭示其在提升系统可维护性、扩展性和灵活性方面的优势。同时,本文还将讨论在实施微服务过程中可能遇到的挑战,如服务治理、分布式事务和数据一致性等问题,并分享相应的解决策略和最佳实践。通过阅读本文,读者将能够理解微服务架构的核心价值,并具备将其应用于实际项目的能力。 ##

热门文章

最新文章

相关产品

  • 函数计算