[函数计算] 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行代码。更重要的是这个服务是具有弹性伸缩和高可用能力的,即使有上千个包要分发也可以轻松应对。

相关实践学习
基于函数计算一键部署掌上游戏机
本场景介绍如何使用阿里云计算服务命令快速搭建一个掌上游戏机。
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
6天前
|
Java API 微服务
【Spring Boot系列】通过OpenAPI规范构建微服务服务接口
【4月更文挑战第5天】通过OpenAPI接口构建Spring Boot服务RestAPI接口
|
1月前
|
监控 Serverless 测试技术
Serverless 应用引擎常见问题之做的web服务计费如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
407 3
|
1月前
|
运维 Serverless 调度
Serverless 应用引擎常见问题之资源使用完影响服务使用如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
22 0
|
30天前
|
监控 Serverless
函数计算(FC)作为一种无服务器的计算服务,在使用过程中可能会遇到各种问题
函数计算(FC)作为一种无服务器的计算服务,在使用过程中可能会遇到各种问题
21 4
|
1月前
|
SpringCloudAlibaba Java 网络架构
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
98 0
|
13天前
|
监控 Sentinel 微服务
微服务的防御之道:服务雪崩、服务熔断、服务降级
微服务的防御之道:服务雪崩、服务熔断、服务降级
20 1
|
26天前
|
人工智能 监控 Serverless
如何基于ACK Serverless快速部署AI推理服务
通过上述步骤,可以在ACK Serverless上快速部署AI推理服务,实现高可用、弹性扩展的服务架构。
19 1
|
27天前
|
Java fastjson 数据安全/隐私保护
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
39 0
|
1月前
|
关系型数据库 MySQL Serverless
Serverless 应用引擎常见问题之新发布的服务 arms 没了如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
25 3
|
1月前
|
消息中间件 SpringCloudAlibaba Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
783 0

相关产品

  • 函数计算