[函数计算] 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 架构模式
目录
相关文章
|
10天前
|
监控 Java 测试技术
现代化软件开发中的微服务架构设计与实践
随着软件开发的发展,传统的单体应用架构已经无法满足现代化应用的需求。微服务架构作为一种新的设计理念,为软件开发提供了更灵活、可扩展的解决方案。本文将介绍微服务架构的设计原则、实践方法以及相关技术工具,并结合实例展示其在现代化软件开发中的应用。
|
1天前
|
存储 监控 API
构建高效微服务架构:后端开发的现代实践
【5月更文挑战第9天】 在本文中,我们将深入探讨如何在后端开发中构建一个高效的微服务架构。通过分析不同的设计模式和最佳实践,我们将展示如何提升系统的可扩展性、弹性和维护性。我们还将讨论微服务架构在处理复杂业务逻辑和高并发场景下的优势。最后,我们将分享一些实用的工具和技术,以帮助开发者实现这一目标。
|
2天前
|
监控 API 持续交付
构建高效可靠的微服务架构:策略与实践
【5月更文挑战第8天】在当今快速演进的软件开发领域,微服务架构已经成为实现敏捷开发、持续交付和系统弹性的关键模式。本文将探讨构建一个高效且可靠的微服务系统所必须的策略和最佳实践。我们将从服务的划分与设计原则出发,讨论如何通过容器化、服务发现、API网关以及断路器模式来优化系统的可伸缩性和鲁棒性。此外,我们还将涉及监控、日志管理以及CI/CD流程在确保微服务架构稳定运行中的作用。
|
2天前
|
敏捷开发 持续交付 API
构建高效微服务架构:后端开发的现代实践
【5月更文挑战第8天】 在数字化转型的浪潮中,微服务架构已成为企业追求敏捷开发、持续交付和系统弹性的关键解决方案。本文将深入探讨微服务的核心概念,包括其设计原则、优缺点以及如何在后端开发中实现高效的微服务架构。我们将通过实际案例分析,展示微服务如何帮助企业快速适应市场变化,同时保持系统的可维护性和扩展性。
|
4天前
|
监控 负载均衡 数据安全/隐私保护
探索微服务架构下的服务网格(Service Mesh)实践
【5月更文挑战第6天】 在现代软件工程的复杂多变的开发环境中,微服务架构已成为构建、部署和扩展应用的一种流行方式。随着微服务架构的普及,服务网格(Service Mesh)作为一种新兴技术范式,旨在提供一种透明且高效的方式来管理微服务间的通讯。本文将深入探讨服务网格的核心概念、它在微服务架构中的作用以及如何在实际项目中落地实施服务网格。通过剖析服务网格的关键组件及其与现有系统的协同工作方式,我们揭示了服务网格提高系统可观察性、安全性和可操作性的内在机制。此外,文章还将分享一些实践中的挑战和应对策略,为开发者和企业决策者提供实用的参考。
|
4天前
|
API 持续交付 开发者
构建高效微服务架构:策略与实践
【5月更文挑战第6天】随着现代软件系统的复杂性增加,微服务架构逐渐成为企业开发的首选模式。本文深入分析了构建高效微服务架构的关键策略,并提供了一套实践指南,帮助开发者在保证系统可伸缩性、灵活性和稳定性的前提下,优化后端服务的性能和可维护性。通过具体案例分析,本文将展示如何利用容器化、服务网格、API网关等技术手段,实现微服务的高可用和敏捷部署。
|
5天前
|
缓存 NoSQL Java
构建高性能微服务架构:Java后端的实践之路
【5月更文挑战第5天】在当今快速迭代和高并发需求的软件开发领域,微服务架构因其灵活性、可扩展性而受到青睐。本文将深入探讨如何在Java后端环境中构建一个高性能的微服务系统,涵盖关键的设计原则、常用的框架选择以及性能优化技巧。我们将重点讨论如何通过合理的服务划分、高效的数据存储策略、智能的缓存机制以及有效的负载均衡技术来提升整体系统的响应速度和处理能力。
|
5天前
|
监控 持续交付 数据库
构建高效可靠的微服务架构:策略与实践
【5月更文挑战第5天】 在当今快速发展的软件开发领域,微服务架构已成为构建可扩展、灵活且容错的系统的首选模式。本文将探讨如何通过一系列经过验证的策略和最佳实践来构建一个高效且可靠的微服务系统。我们将深入分析微服务设计的核心原则,包括服务的细粒度划分、通信机制、数据一致性以及容错处理,并讨论如何利用现代技术栈来实现这些目标。文章将提供一套综合指南,旨在帮助开发者和架构师在保证系统性能的同时,确保系统的稳健性。
23 4
|
6天前
|
消息中间件 Go API
Golang深入浅出之-Go语言中的微服务架构设计与实践
【5月更文挑战第4天】本文探讨了Go语言在微服务架构中的应用,强调了单一职责、标准化API、服务自治和容错设计等原则。同时,指出了过度拆分、服务通信复杂性、数据一致性和部署复杂性等常见问题,并提出了DDD拆分、使用成熟框架、事件驱动和配置管理与CI/CD的解决方案。文中还提供了使用Gin构建HTTP服务和gRPC进行服务间通信的示例。
22 0
|
8天前
|
Serverless 云计算 Docker
SAE是全场景Serverless计算平台,深度融合微服务
【5月更文挑战第2天】SAE是全场景Serverless计算平台,深度融合微服务,提供SAE Job任务场景解决方案,具备便捷、节省、稳定、透明和省心的特点。而ECI是Serverless容器运行服务,结合云计算理念,仅需Docker镜像即可运行,支持细粒度资源计费,旨在降低成本和提升效率。SAE侧重应用管理和运营,ECI专注于优化容器资源使用。
17 0

热门文章

最新文章

相关产品

  • 函数计算