AngryRED

Easier & Better

腾讯云无服务器云函数实践

20 Nov 2018 » scf

从传统的服务器到近年风生水起的云服务器微服务架构,再到无服务器(Serverless)架构,开发者们的探索之路永不停歇!

虽然各家的名称都不一样,但总体来说都是基于FaaS(Function as a Service),所有的事情都是基于函数去做。

它可以结合API网关来调用,进而达到传统服务器的功能。

优点

  1. 不需要管理,优化服务器,什么弹性伸缩,负载均衡都不用自己去做。
  2. 相对安全,可靠,因为这些事情都由产商来完成。
  3. 可用度高,触发执行,有需要的时候才会执行,空闲0成本。
  4. 开发者可以只关注业务逻辑,其它的一概不管。

由于买了腾讯的云服务器,顺带就试了一下如何创建腾讯分无服务器云函数。

支持的语言有:Python2.7, Python3.6, Nodejs6.10, Nodejs8.9, Java8, Php5.6, Php7.2, Golang1

开发步骤:

  1. 无服务器云函数控制台新建函数服务。
  2. 上传自己的代码(jar或zip)。
  3. 测试

注意事项:

  1. Java版本是8.0
  2. Java执行方法格式为:[package].[class]::[method],注意包名只能是一级,还不支持多级包名(好像有坑,😁)。
  3. 日志:System.out.print就可以,或者使用java.util.logging,由于无法联调,日志信息很重要。

Demo

public class Function {

	public String handle(Input input) {
		System.out.println("-----------------");
		System.out.println("Input: " + input);
		if (input == null || input.getName() == null) {
			return "发生错误";
		}
		String result = input.getName().toUpperCase();
		System.out.println("Result: " + result);
		return result;
	}
}

public class Input {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

测试模板:

{"name":"abcd"}

日志:


START RequestId: f389b1c9-edff-11e8-b97c-525400c7c826

Event RequestId: f389b1c9-edff-11e8-b97c-525400c7c826Event:{

  "name": "abcd"

}

-----------------

Input: demo.Input@4de8b406

Result: ABCD


END RequestId: f389b1c9-edff-11e8-b97c-525400c7c826

Report RequestId: f389b1c9-edff-11e8-b97c-525400c7c826 Duration:854.408ms Memory:128MB MaxMemoryUsed:31.168MB

说实话腾讯云的bug还真不少,网页控制台里面居然没有String类型的模板,非得用JSON,😁。 不过也好,像这样简单的例子实际中也不存在。

编写函数代码总结

一,事件入参(函数中的第一个变量)

  • Java基础类型包括String
  • POJO类型,必须有公有getter和setter方法。

二,Context入参(函数中的第二个变量为Context类型)

import com.qcloud.scf.runtime.Context
...
public String handle(Input input, Context context) {
    //
}

Context是什么?每家的函数服务都会传一个Context参数进去,一般是这个函数相关的一些变量,如名称,内存配置等,有些也会直接传一个logger对象进去, 腾讯云的Context现在还比较鸡肋:

package com.qcloud.scf.runtime;

public abstract interface Context {
	public abstract String getRequestId();

	public abstract int getTimeLimitInMs();

	public abstract int getMemoryLimitInMb();

	// public abstract String getFunctionName();

	// public abstract String getFunctionVersion();

	// public abstract String getMemoryLimitInMb();

	// public abstract String getTimeLimitInMs();

	// public abstract String getLogGroupName();

	// public abstract String getLogStreamName();

}

三,返回结果可以是Java基础类,String和POJO。

四, Maven打包,需要用shade插件将所有jar包打到一起

<build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.3</version>
        <configuration>
          <createDependencyReducedPom>false</createDependencyReducedPom>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
</build>

SDK的使用

腾讯云SDK在Github上有源码开发,是已经封装好了的API,也可以通过Maven来引用:

<dependency>
	<groupId>com.tencentcloudapi</groupId>
	<artifactId>tencentcloud-sdk-java</artifactId>
	<version>3.0.29</version>
</dependency>

首先:实例化认证对象,需要腾讯云账户secretIdsecretKey

// 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey
Credential credential = new Credential(secretId, secretKey);
ClientProfile profile = new ClientProfile(ClientProfile.SIGN_SHA256);
ScfClient scfClient = new ScfClient(credential, region, profile);

然后通过:CreateFunctionRequestDeleteFunctionRequestListFunctionsRequest等进行函数的创建,删除等操作。

CreateFunctionRequest createRequest = new CreateFunctionRequest();
if (uploaded) {
  Code code = new Code();
  code.setCosBucketName(function.getBucket().getName());
  code.setCosBucketRegion(function.getBucket().getRegion());
  code.setCosObjectName(functionName);
  createRequest.setCode(code);
}
createRequest.setDescription(function.getDescription());
createRequest.setRuntime(RUNTIME);
createRequest.setFunctionName(functionName);
createRequest.setHandler(handler);
try {
  scfClient.CreateFunction(createRequest);
} catch (TencentCloudSDKException e) {
  throw e;
}

源码上传

源码上传有2中方式:

  1. 直接将打好的jar包或zip包通过网页控制台上传。
  2. 通过COS(腾讯云对象存储)上传。 通过COS上传,首先要把你的jar包或zip包上传到对象存储,然后在控制台中进行关联就可以了

注意的一点: 通过SDK+COS上传代码的时候,COS的bucketName不要带后面的appid,一般情况下载对象存储中名称是有[name]-[appid]组成的。

上传的代码如下:

COSCredentials cred = new BasicCOSCredentials(function.getSecretId(), function.getSecretKey());
ClientConfig clientConfig = new ClientConfig(new Region(bucketRegion));
COSClient cosClient = new COSClient(cred, clientConfig);
if (!cosClient.doesBucketExist(bucketName)) {
  CreateBucketRequest request = new CreateBucketRequest(bucketSimpleName);
  cosClient.createBucket(request);
  cosClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
} else {
  cosClient.putObject(bucketName, function.getName(), mavenProject.getArtifact().getFile());
}
cosClient.shutdown();

注意:这里的bucketName一定要是全名:([name]-[appid])。

但是如果用SDK更新云函数:一定记得不要带[appid],只使用前面的[name]作为bucketName

Credential credential = new Credential(secretId, secretKey);
ClientProfile profile = new ClientProfile(ClientProfile.SIGN_SHA256);
ScfClient scfClient = new ScfClient(credential, region, profile);
UpdateFunctionCodeRequest updateCodeRequest = new UpdateFunctionCodeRequest();
updateCodeRequest.setFunctionName(functionName);
updateCodeRequest.setHandler(handler);
updateCodeRequest.setCosBucketName(bucketName);
updateCodeRequest.setCosBucketRegion(bucketRegion);
updateCodeRequest.setCosObjectName(objectName);

scfClient.UpdateFunctionCode(updateCodeRequest);

曾经因为这个问题,提了工单进行了好多次的重试。切记

更多: