在 2015年的LinuxCon/ContainerCon 上我呈现了一次演示驱动的演讲,标题叫做“没有服务器的微型服务 ”。 其中,我创建了一个图片处理的微型服务,将其部署到了多个区域,构建了一个移 动 app 并使用它(译者注:指的是这个微型服务)作为后台,添加了一个使用了 Amazon API 网关的基于 HTTP 的 API 和一个网站,并且对它进行了单元和负载测试,所有这些都没有用到任何 服务器。 这篇博文对演讲的细节进行了重制,为你逐步完成所有必要的比周,并深入到了架构中去。而高层次的概述,可以看看这里的 幻灯片。还有这一架构的另外一个示例,可以看看这个可执行的 gist 资源库,SquirrelBin。 |
无服务器架构
|
下面的代码是一种“hello world” 类型的程序,用来演示 ImageMagick —— 它给我提供了一个基础的命令架构 (又叫做 switch 语句) 并且让我们可以获取到内置的玫瑰图片并返回它。除了对结果进行编码,那样它就可以很好的以 JSON 的形式存在,做这个并没有太多东西。
首先,让我们确保服务是运行着的,可以通过在 AWS Lambda 控制台的测试窗口向它发送下面的 JSON:
你应该会得到必要的 “pong” 回应。接下来,我们将通过发送像下面这样的 JSON 来实际调用到 ImageMagick :
这一请求获取的是表示一张 PNG 版本玫瑰图片的 base64 编码的字符串: “”iVBORw0KGg…Jggg==”. 为了确认这个并不只 是一些随机的字符, 将它复制粘贴(没有双引号) 到任何方便使用的 Base64-到-图片 解码器, 比如 codebeautify.org/base64-to-image-converter. 你应该能看到一张漂亮的玫瑰图片:
样例图片 (红玫瑰) |
现在,让我们通过打开它的 Nodejs 包的剩余部分来完成图像处理服务。我们将提供一些不同的操作:
|
大部分的代码是非常简单的由 Nodejs ImageMagick 封装的程序,其中一些以 JSON 方式编码(在这种情况下,传递给 Lambda 的事件被清理并向前传 递),另一些以命令行(又名“自定义”)参数方式传递一个字符串数组。如果你之前从来没有使用过 ImageMagick,那么,ImageMagick 作为命令行的包装器并且文件名具有语 义含义的要求可能是不被引起注意的。 我们有两个相互矛盾的需求:我们希望客户端传递语法格式(例如,输出图像的格式是 PNG 或者是 JPEG),但我们同时也要求服务器来决定在磁盘上何 处放置临时存储,以便我们不遗漏具体的实现细节。为了同时实现这两个目标,我们在 JSON 模式中定义了两个参数:“inputExtension” 和“outputExtension”, 然后,我们通过将客户端的部分(文件扩展名)与服务器的部分(目录和基名)相结合,构建实际的文件位置。你可以看到(和使用!)图像处理计划大纲(image processing blueprint )的已完成代码。 |
有很多测试你都可以在这里运行(我们也会在后面做更多工作),但就像一个快速而明智的检测一样,检索一个样本会再次创建图像并使用一个否定(颜 色转变)过滤器将其回传。你可以在 Lambda 窗体中使用 JSON 这类工具,仅仅是用实际的图像单元替代基于 64 位的图像场(要在这个博客页面下包含这个有点长)。
输出,解码为一个图像,应该是一个难懂的植物珍品,一个蓝玫瑰: 蓝色玫瑰(红色玫瑰样品图像的底片) 因此这所有的是服务的函数方面的内容。通常,在这个地方起初会变得丑陋,从“一次工作”到“具备 24x7x365 监控和生产记录的 可伸缩和可靠的服务“。但这就是 Lambda 的漂亮所在:我们的图像进程代码已经是被完全摧毁了的,生产强度也是微服务。接下来,让我们加入一个可以寻呼的移动 app 吧... |
||
步骤2: 创建一个移动客户端我们的图像处理微服务可以以多种方式访问,但是为了展示一个样板客户端,我们将建立一个快速的Android app。下面我展示的客户端代码,是我们在 ContainerCon 演讲中创建的一个简单的 Android 应用程序。它允许你选择一个图像和一个滤波器,然后通过调用运行在 AWS Lambda 的图像处理服务的“转换”操作,最终显示 使用过滤器处理后的图像效果。 下面的场景显示了应用程序的工作原理,其中一个是它的示例图片 --AWS Lambda 的图标: Android 模拟器显示 AWS Lambda 的图标 我们将选择“相反(negate) ”过滤器来反转图标的颜色:
选择“相反(negate)”图像转换滤波器 下面是结果:一个蓝色版本的 Lambda 图标(原始版本为橙色):
使用“相反(negate)”滤镜处理后的 AWS Lambda 图标的结果 我们还可以选择西雅图照片并使用深褐色滤镜处理,使得图片中的现代的西雅图天空有一种怀旧感
深褐色滤镜处理后的西雅图天空。 |
现在回到代码上面来吧。这里我不会试着去教授基础的 Android 编程,只特地专注于这个应用的 Lambda 元素。(如果你在创建自己的应用,你也会需要 包含 AWS Mobile SDK 的 jar 包 ,以运行下面的示例代码) 。从概念上来讲有这么四个部分:
我们将会逐一地来看看各个部分。 数模模式定义了任何需要在客户端和服务器之间进行传递的对象。这里没有“Lambda形式”的东西; 这些对象都只是 POJO(普通的 Java 对象),没有特殊的库或者框架。我们定义了一个基础事件,然后对它进行了扩展以反映我们的操作结构 – 你可以把这当做是之前我们定义和测试图像处理服务所用到的 JSON 的 “Java 状态”。如果你也在使用 Java 编写服务端,那你通常就应该会把这些文件共享出来作为通用时间结构定义的一部分;在我们的示例中,这些 POJO 会在服务端被转换成 JSON。 |
LambdaEvent.java
|
1
2
3
4
5
6
|
package com.amazon.lambda.androidimageprocessor.lambda; public class LambdaEvent { private String operation; public String getOperation() { return operation;} public void setOperation(String operation) { this .operation = operation;} public LambdaEvent(String operation) {setOperation(operation);}} |
ImageConvertRequest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.amazon.lambda.androidimageprocessor.lambda; import java.util.List; public class ImageConvertRequest extends LambdaEvent { private String base64Image; private String inputExtension; private String outputExtension; private List customArgs; public ImageConvertRequest() { super ( "convert" );} public String getBase64Image() { return base64Image;} public void setBase64Image(String base64Image) { this .base64Image = base64Image;} public String getInputExtension() { return inputExtension;} public void setInputExtension(String inputExtension) { this .inputExtension = inputExtension;} public String getOutputExtension() { return outputExtension;} public void setOutputExtension(String outputExtension) { this .outputExtension = outputExtension;} public List getCustomArgs() { return customArgs;} public void setCustomArgs(List customArgs) { this .customArgs = customArgs;}} |
到目前为止还不是很复杂。现在我们有了一个数据模型,再就是将要使用一些 Java 注解来定义服务端点。这里我们会暴露出两个操作, “ping” 以及“convert”; 这也能很容易通过添加其它注解来对其进行扩展,但就下面这个示例应用而言,我们暂时还不需要这么做。
ILambdaInvoker.java
1
2
3
4
5
6
7
8
|
package com.amazon.lambda.androidimageprocessor.lambda; import com.amazonaws.mobileconnectors.lambdainvoker.LambdaFunction; import java.util.Map; public interface ILambdaInvoker { @LambdaFunction (functionName = "ImageProcessor" ) String ping (Map event); @LambdaFunction (functionName = "ImageProcessor" ) String convert (ImageConvertRequest request);} |
现在我们已经准备好来做这个应用主要部分了。这里大部分都是样板式的 Android 代码或者简单客户端资源管理,而我将会点出几个跟 Lambda 相关的 部分:
这就是“init”部分;它创建了身份验证功能来调用 Lambda API 并创建了一个能够调用上面所定义的端点,而且能在我们的数据模型中传 送 POJO 的 Lambda 调用:
1
2
3
4
5
6
7
8
|
// Create an instance of CognitoCachingCredentialsProvider CognitoCachingCredentialsProvider cognitoProvider = new CognitoCachingCredentialsProvider( this .getApplicationContext(), "us-east- 1:<YOUR COGNITO IDENITY POOL GOES HERE>" , Regions.US_EAST_1); // Create LambdaInvokerFactory, to be used to instantiate the Lambda proxy.
LambdaInvokerFactory factory = new LambdaInvokerFactory( this .getApplicationContext(), Regions.US_EAST_1, cognitoProvider); // Create the Lambda proxy object with a default Json data binder. lambda = factory.build(ILambdaInvoker. class ); |
其余的也挺有趣的部分代码就是它自身实际的远程过程调用了:
1
2
3
4
5
6
|
try { return lambda.convert(params[0]); } catch (LambdaFunctionException e) { Log.e( "Tag" , "Failed to convert image" ); return null ; } |
实际上也不那么有趣,因为这戏法(参数序列化和结果的反序列化)是发生在幕后的,留给我们的仅仅只是一些错误的处理而已。
本文转自:开源中国社区 [http://www.oschina.net]
本文标题:无服务器的微服务
本文地址:http://www.oschina.net/translate/microservices-without-the-servers
参与翻译:Iam魔方 , leoxu, HAILINCAI, 武汉加油, 无若, 木兰宿莽
英文原文:Microservices without the Servers