开发者社区> 问答> 正文

如何使用C#开发函数计算?

如何使用C#开发函数计算?

展开
收起
小天使爱美 2020-03-27 16:13:13 1528 0
1 条回答
写回答
取消 提交回答
  • 在函数计算中使用 C# 编程,您需要定义一个 C# 编写的函数作为入口。本文详细介绍了 C# 的函数入口定义项。

    函数入口概述 C# 运行环境(dotnetcore2.1)根据是否支持 HTTP 触发器分为 普通函数入口 和 设置 HTTP 触发器 两种函数入口,为函数设置 HTTP 触发器后的函数入口形式会不同,这是为了方便处理发来的 HTTP request 请求, 同时还有相应的 initializer 入口。

    普通函数 函数入口定义 Handler 方法示例 Handler 规范 普通函数完整操作示例 initializer 入口 设置 HTTP 触发器的函数 函数入口定义 HTTP 触发器的函数入口示例 HTTP 触发器的函数入口限制项

    普通函数入口

    函数入口定义 当创建一个基于 C# 的函数时,需要指定一个 handler 方法,该方法在函数执行时被执行。这个handler 方法可以是 static 方法或者 instance 方法,如果想在该方法中访问 IFcContext 对象,则可以将该方法中的第二个参数指定为 IFcContext 对象。支持的 handler 方法定义如下:

    ReturnType HandlerName(InputType input, IFcContext context); //包含IFcContext ReturnType HandlerName(InputType input); // 不包含IFcContext Async Task HandlerName(InputType input, IFcContext context); Async Task HandlerName(InputType input); 函数计算支持在使用 C# 编写的函数中应用 Async, 此时函数的执行会等待异步方法执行结束。

    在上述定义中:

    ReturnType: 返回对像可以是 void (注:此时 Async Task 退化为 async Task), System.IO.Stream 对象或者任何可以被 JSON 序列化和 JSON 反序列化的对象,如果是 Stream对象,则该 Stream 内容直接在响应 Body 返回;否则该返回对象被 JSON 序列化后在响应 Body 返回。 InputType:input 参数可以是 System.IO.Stream 或者 任何可以被 JSON 序列化和 JSON 反序列化的对象。 IFcContext: 函数的 Context 对象,包括以下信息: 参数 类型 描述 RequestId String 当前调用请求的唯一 ID,常用于问题复查或者历史调用计数等。 FunctionParam Class 当前调用的函数的基本信息,如函数名、函数入口、函数内存和超时时间等。 Credentials Class 函数计算服务通过扮演您提供的 服务角色 获得的一组临时密钥 securityToken,每 15 分钟更新一次。您可以在函数代码中使用临时密钥去访问其他阿里云服务,例如 OSS,避免您将重要的身份凭证 AccessKey 写死在函数代码里。 ServiceMeta Class 当前调用的函数所在的服务的信息,包括服务名称,接入的日志服务的 logProject 和 logStore 信息, service 的版本信息 qualifier 和 version_id,qualifier 表示调用函数时指定的 service 版本或别名,version_id 表示实际调用的 service 版本。 Region String 当前调用的函数所在地域,如 cn-shanghai。更多详情,请参阅 地域与可用区。 AccountId String 当前调用函数用户的阿里云账号 ID。更多详情,请参阅 获取账号ID。 更多详情请参考:fc-dotnet-libs

    Handler方法示例 函数计算使用 C# 编写函数, 需要 Nuget 引入 Aliyun.Serverless.Core package.

    Stream Handler 以下方法将用户请求中的输入原样返回。

    using System.IO; using System.Threading.Tasks; using Aliyun.Serverless.Core; using Microsoft.Extensions.Logging; namespace FC.Examples { public class TestHandler { public async Task Echo(Stream input, IFcContext context) { ILogger logger = context.Logger; logger.LogInformation("Handle request: {0}", context.RequestId); MemoryStream copy = new MemoryStream(); await input.CopyToAsync(copy); copy.Seek(0, SeekOrigin.Begin); return copy; } } } POCO Handler 除了 Stream 作为输入输出参数,POCO(Plain old CLR objects)对象同样也可以作为输入和输出。如果该 POCO 没有指定特定的 JSON Serializer 对象,则函数计算默认用 Json.Net 进行对象的 JSON Serialize 以及Deserialize。

    using Microsoft.Extensions.Logging; namespace FC.Examples { public class TestHandler { public class Product { public string Id { get; set; } public string Description { get; set; } } // optional serializer class, if it’s not specified, the default serializer (based on JSON.Net) will be used.
    // [FcSerializer(typeof(MySerialization))]
    public Product Echo(Product product, IFcContext context) { string Id = product.Id; string Description = product.Description; context.Logger.LogInformation("Id {0}, Description {1}", Id, Description); return product; } } }

    Handler 规范 命名格式 在创建函数时,你需要指定一个 handler 方法的字符串,用来告诉函数计算调用哪个方法,该字符串格式如下:AssemblyFileName::FullClassName::METHOD

    其中

    AssemblyFileName 是该函数所在的 Assembly 的文件名(省去.dll) FullClassName 是该函数所在类的全名,Namespace.ClassName Method 是该方法的名字 在上述 Handler 例子中,如果 Assembly 文件为 test_assembly, 则其 handler 字符串为:test_assembly::FC.Examples.TestHandler::Echo

    限制 Handler 参数格式严格按照上述定义,也就是说参数 1 为必须输入,参数 2 可选,但必须为 IFcContext。 Handler 函数不支持 Generic Method。 输入输出参数必须为 Stream 或者 可JSON序列化。 Async函数返回值 Task 中 T 必须为 Stream 或者 可JSON序列化的类。 Custom Serializer 函数计算针对 POCO Handler 提供了默认的基于JSON .NET Serializer,如果默认的 Serializer 不能满足需求, 可以基于 Aliyun.Serverless.Core 中的 interface IFcSerializer 实现Custom Serializer

    public interface IFcSerializer { T Deserialize (Stream requestStream); void Serialize (T response, Stream responseStream); }

    普通函数完整操作示例 临时密钥用于辨识请求者身份和权限,在访问其他服务,例如 OSS 时,您必须设置 securityToken。下面的示例 C# 代码使用临时密钥,向 OSS 的一个 Bucket 获取指定的一个 object:

    创建一个 .net core 的 console 工程

    [songluo@~/tmp]# mkdir fcdotnetsample [songluo@~/tmp]# cd fcdotnetsample [songluo@~/tmp/fcdotnetsample]# dotnet new console 在 fcdotnetsample.csproj 中添加如下 package:

    编辑 Program.cs

    using System; using System.IO; using Aliyun.OSS; using Aliyun.Serverless.Core; namespace fcdotnetsample { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } public class OssFileHandlerRequest { public string Bucket; public string Key; public string Endpoint; } public class OSSFileHandler { public Stream GetOssFile(OssFileHandlerRequest req, IFcContext context) { if (req == null) { throw new ArgumentNullException(nameof(req)); } if (context == null || context.Credentials == null) { throw new ArgumentNullException(nameof(context)); } OssClient ossClient = new OssClient(req.Endpoint, context.Credentials.AccessKeyId, context.Credentials.AccessKeySecret, context.Credentials.SecurityToken); OssObject obj = ossClient.GetObject(req.Bucket, req.Key); return obj.Content; } } } publish 工程并将目标文件打成 zip 包

    [songluo@~/tmp/fcdotnetsample]# dotnet publish -c Release Microsoft (R) Build Engine version 15.9.20+g88f5fadfbe for .NET Core Copyright (C) Microsoft Corporation. All rights reserved. Restore completed in 47.9 ms for /Users/songluo/tmp/fcdotnetsample/fcdotnetsample.csproj. fcdotnetsample -> /Users/songluo/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/fcdotnetsample.dll fcdotnetsample -> /Users/songluo/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish/ [songluo@~/tmp/fcdotnetsample]# cd /Users/songluo/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish/ [songluo@~/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish]# zip -r fcdotnetsample.zip * adding: Aliyun.OSS.Core.dll (deflated 60%) adding: Aliyun.Serverless.Core.dll (deflated 59%) adding: Microsoft.Extensions.Logging.Abstractions.dll (deflated 53%) adding: fcdotnetsample.deps.json (deflated 73%) adding: fcdotnetsample.dll (deflated 57%) adding: fcdotnetsample.pdb (deflated 27%) adding: fcdotnetsample.runtimeconfig.json (deflated 23%) [songluo@~/tmp/fcdotnetsample/bin/Release/netcoreapp2.1/publish]# ls -ll fcdotnetsample.zip -rw-r--r-- 1 songluo staff 130276 Mar 14 17:48 fcdotnetsample.zip 后面直接使用这个 fcdotnetsample.zip 创建 runtime 为 dotnetcore2.1, handler 为 fcdotnetsample::fcdotnetsample.OSSFileHandler::GetOssFile 的函数就行。

    initializer 入口 函数计算提供了 Init 方法的机制,用于执行初始化工作。该 Init 方法会自动在后台容器启动时被调用,每个容器只调用一次。Init 方法定义:

    public void Init(); //没有context对象 public void Init(IFcContext context); //包含context对象 public static void Init(); //没有context对象 public static void Init(IFcContext context); //包含context对象 initializer 格式 MyInitializer 需要与添加 initializer 函数时的 “initializer” 字段相对应:例如创建函数时指定的 initializer 入口为 fcdotnetsample::fcdotnetsample.TestHandler::MyInitializer,那么函数计算在配置 initializer 功能后会首先加载 fcdotnetsample.TestHandler 中定义的 MyInitializer 函数。 initializer 特点 IFcContext 中的 FunctionParam 中 FunctionInitializer 和 InitializationTimeout 两个信息是为 initializer 设计的,当使用 initializer 功能时,会被设置为用户创建函数时所设置的值,否则为空,且不生效。 无返回值。在函数末尾增加 return 操作是无效的。

    HTTP 触发器的函数入口 设置了 HTTP 触发器的函数入口与其他触发器要求的函数入口不同,以下为一个基本的 HTTP 触发器规定的函数入口定义:

    函数计算使用 C# 编写 HTTP 触发器的函数, 需要 Nuget 引入 Aliyun.Serverless.Core 和 Aliyun.Serverless.Core.Http package.

    using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Aliyun.Serverless.Core; using Aliyun.Serverless.Core.Http; namespace MySpace.TestHandlers { public class SingleHttpHandler : FcHttpEntrypoint { protected override void Init(IWebHostBuilder builder) { } public override async Task HandleRequest(HttpRequest request, HttpResponse response, IFcContext fcContext) { response.StatusCode = 200; response.ContentType = "text/plain"; await response.WriteAsync("hello world"); return response; } } } 函数入参

    IFcContext 参数与普通函数接口的 IFcContext 接口相同。

    HttpRequest

    HttpResponse

    说明

    C# 编写 HTTP 触发器的函数必须继承 Aliyun.Serverless.Core.Http 中的 FcHttpEntrypoint, 其中 Init 函数必须 override, HandleRequest 是函数入口 handler, 可以根据情况决定是否 override

    Single function: override HandleRequest, HandleRequest 实现自定义的逻辑处理

    Asp.net core application: 只需要 override Init 函数

    下节的示例会具体描述怎么使用 FcHttpEntrypoint

    HTTP 触发器的函数入口示例 Single function 示例 以下示例示范了如何使用 HTTP 触发器的函数入口中的 HttpRequest 和 HttpResponse:

    using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Aliyun.Serverless.Core; using Aliyun.Serverless.Core.Http; using Microsoft.Extensions.Logging; namespace MySpace.TestHandlers { public class SingleHttpHandler : FcHttpEntrypoint { protected override void Init(IWebHostBuilder builder) { } public override async Task HandleRequest(HttpRequest request, HttpResponse response, IFcContext fcContext) { string method = request.Method; string relativePath = request.Path.Value; fcContext.Logger.LogInformation("method = {0}; requestPath = {1}", method, relativePath); StreamReader sr = new StreamReader(request.Body); string requestBody = sr.ReadToEnd(); fcContext.Logger.LogInformation("requestBody = {}", requestBody); // process request.Headers response.StatusCode = 200; response.Headers["Content-Type"]="text/plain"; response.Headers.Add("customheader", "v1"); await response.WriteAsync("hello world"); return response; } } } Asp.net core application 示例 using System; using Aliyun.Serverless.Core.Http; using Microsoft.AspNetCore.Hosting; namespace MySpace.TestWebApi { public class FcRemoteEntrypoint : FcHttpEntrypoint { protected override void Init(IWebHostBuilder builder) { builder .UseStartup (); } } } 具体操作 创建一个 asp.net core 的 webapi 工程

    [songluo@~/tmp]# mkdir fcaspdotnetsample [songluo@~/tmp]# cd fcaspdotnetsample [songluo@~/tmp/fcaspdotnetsample]# dotnet new webapi 在 fcaspdotnetsample.csproj 中添加如下 package:

    新建文件 FcRemoteEntrypoint.cs, 文件内容为 Asp.net core application 示例代码

    publish 工程并将目标文件打成 zip 包

    [songluo@~/tmp/fcaspdotnetsample]# dotnet publish -c Release Microsoft (R) Build Engine version 15.9.20+g88f5fadfbe for .NET Core Copyright (C) Microsoft Corporation. All rights reserved. Restore completed in 88.39 ms for /Users/songluo/tmp/fcaspdotnetsample/fcaspdotnetsample.csproj. fcaspdotnetsample -> /Users/songluo/tmp/fcaspdotnetsample/bin/Release/netcoreapp2.1/fcaspdotnetsample.dll fcaspdotnetsample -> /Users/songluo/tmp/fcaspdotnetsample/bin/Release/netcoreapp2.1/publish/ [songluo@~/tmp/fcaspdotnetsample]# cd /Users/songluo/tmp/fcaspdotnetsample/bin/Release/netcoreapp2.1/publish/ [songluo@~/tmp/fcaspdotnetsample/bin/Release/netcoreapp2.1/publish]# zip -r fcaspdotnetsample.zip * adding: appsettings.Development.json (deflated 40%) adding: appsettings.json (deflated 30%) adding: fcaspdotnetsample.deps.json (deflated 85%) adding: fcaspdotnetsample.dll (deflated 61%) adding: fcaspdotnetsample.pdb (deflated 40%) adding: fcaspdotnetsample.runtimeconfig.json (deflated 31%) adding: web.config (deflated 40%) [songluo@~/tmp/fcaspdotnetsample/bin/Release/netcoreapp2.1/publish]# ls -ll fcaspdotnetsample.zip -rw-r--r-- 1 songluo staff 39101 Mar 15 09:47 fcaspdotnetsample.zip 后面直接使用这个 fcaspdotnetsample.zip 创建 runtime 为 dotnetcore2.1, handler 为 fcaspdotnetsample::MySpace.TestWebApi.FcRemoteEntrypoint::HandleRequest 的函数就行。

    如果使用 Single function, 参考 普通函数完整操作示例, 创建 console 工程,新建 FcRemoteEntrypoint.cs, 代码改成 Single function 示例代码即可。

    HTTP 触发器的函数入口限制项

    Request 限制项 如果 HTTP 触发器的函数入口 Request 超过以下限制,会抛出 400 状态码和 InvalidArgument 错误码

    参数 限制 HTTP 状态码 错误码 headers headers 中的所有键值对(key 和 value)的大小不能超过 4 KB。 400 InvalidArgument path path 以及所有 query 参数(params)的大小不能超过 4 KB。 body HTTP body 的大小不能超过 6 MB。

    Response 限制项 如果超过以下限制,会抛出 502 状态码和 BadResponse 错误码。

    参数 限制 HTTP 状态码 错误码 headers headers 中的所有键值对(key 和 value)的大小不能超过 4 KB。 502 BadResponse body HTTP body 的大小不能超过 6 MB。 更多有关 http trigger 的详情,请参考 HTTP 触发器。

    参考链接 有关 .NET core 运行环境的详细信息,请参阅 .NET core 运行环境。

    函数计算支持 .net core 2.1(runtime = dotnetcore2.1)运行环境, 编写函数的语言为 C# 。本文主要介绍 dotnetcore2.1 运行环境相关内容:

    使用 logger 使用第三方库 错误处理

    使用 logger C# 函数通过 context.Logger 打印的内容会被收集到创建服务时指定的日志服务 Logstore 中。

    日志级别 您可以通过改变 logger 的 property EnabledLogLevel 达到改变日志级别目的,其中有如下几种从高到低的日志级别:

    日志级别 Level 接口 Critical 5 context.Logger.LogCritical Error 4 context.Logger.LogError Warning 3 context.Logger.LogWarning Information 2 context.Logger.LogInformation Debug 1 context.Logger.LogDebug Trace 0 context.Logger.LogTrace 更多有关日志 Level 的信息, 请参考:LogLevel Enum

    更多有关函数日志的详情,请参阅 函数日志。

    logger 示例一 using System; using System.IO; using System.Text; using Aliyun.Serverless.Core; using Microsoft.Extensions.Logging; namespace FC.Examples { public class TestLogger { public Stream Echo(Stream input, IFcContext context) { context.Logger.LogInformation(string.Format("detail = {0} ", "hello world")); using (MemoryStream output = new MemoryStream(100)) { byte[] hello = Encoding.UTF8.GetBytes("hello world"); output.Write(hello, 0, hello.Length); return output; } } } } 输出的日志内容为:

    2019-03-15T03:09:59.812Z 8ba1a2a2-0eb7-9e79-c3c6-ee6606c5beaf [INFO] detail = hello world logger 示例二 using System; using System.IO; using System.Text; using Aliyun.Serverless.Core; using Microsoft.Extensions.Logging; namespace FC.Examples { public class TestLogger { public Stream Echo(Stream input, IFcContext context) { context.Logger.EnabledLogLevel = LogLevel.Error; context.Logger.LogError("console error 1"); context.Logger.LogInformation("console info 1"); context.Logger.LogWarning("console warn 1"); context.Logger.LogDebug("console debug 1"); context.Logger.EnabledLogLevel = LogLevel.Warning; context.Logger.LogError("console error 2"); context.Logger.LogInformation("console info 2"); context.Logger.LogWarning("console warn 2"); context.Logger.LogDebug("console debug 2"); context.Logger.EnabledLogLevel = LogLevel.Information; using (MemoryStream output = new MemoryStream(100)) { byte[] hello = Encoding.UTF8.GetBytes("hello world"); output.Write(hello, 0, hello.Length); return output; } } } } 输出的日志内容为:

    2019-03-15T03:09:31.047Z f4ddc314-d3e9-91c9-b774-4b08c91a977d [ERROR]: console error 1 2019-03-15T03:09:31.047Z f4ddc314-d3e9-91c9-b774-4b08c91a977d [ERROR]: console error 2 2019-03-15T03:09:31.047Z f4ddc314-d3e9-91c9-b774-4b08c91a977d [WARNING]: console warn 2

    使用第三方库 C# 编写的函数使用第三方库十分简单

    直接编辑对应的 project 的 .csproj 文件, 增加对应的package, 比如:

    使用 Visual Studio IDE, 直接 GUI 操作添加对应 Nuget 包

    错误处理 C# 函数在执行过程中发生异常时,函数计算捕获异常并返回异常信息。以下示例代码返回了 oops 的异常信息:

    using System; using System.IO; using Aliyun.Serverless.Core; namespace FC.Examples { public class TestException { public Stream Echo(Stream input, IFcContext context) { throw new Exception("oops"); } } } 根据以上示例代码,您调用函数时可能收到如下响应信息:

    { "ErrorMessage": "oops", "ErrorType": "System.Exception", "StackTrace": [...] } 发生异常时,函数调用的响应的 HTTP header 中会包含 X-Fc-Error-Type: UnhandledInvocationError。更多有关函数计算的错误类型,请参阅 错误类型。

    2020-03-27 16:28:48
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
Hologres Serverless之路:揭秘弹性计算组 立即下载
Serverless开发平台: 让研发效能再提升 立即下载
Serverless 引领云上研发新范式 立即下载