函数计算新功能-----支持C#函数

本文涉及的产品
对象存储 OSS,20GB 3个月
文件存储 NAS,50GB 3个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: 前言函数计算作为新兴的事件驱动serverless平台正受到越来越多开发者的欢迎,之前已支持Java, Python, Nodejs, Php四种语言。现在函数计算正式支持C#。由于其和Java类似的功能以及和Windows的紧密集成,.Net在中小企业中非常普及,许多中小企业的内部应用都是基于.Net开发。

前言

函数计算作为新兴的事件驱动serverless平台正受到越来越多开发者的欢迎,之前已支持Java, Python, Nodejs, Php四种语言。现在函数计算正式支持C#。由于其和Java类似的功能以及和Windows的紧密集成,.Net在中小企业中非常普及,许多中小企业的内部应用都是基于.Net开发。是时候让这些传统应用拥抱新兴的serverless平台了。.Net平台中目前得到最多关注的便是.Net Core,它是目前微软主推的真正的跨平台语言。自.Net core 2.1之后,现有的.Net程序可以非常方便的移植。目前函数计算支持的.Net版本便是.Net Core 2.1。

功能介绍

在函数计算中运行.net程序分为两种模式,一种是以Library形式运行用户提供的函数--我们称之为Normal Invoke,主要面向计算场景,可以对接除http trigger以外的其他trigger;另一种是以Web App形式运行用户提供的Asp.Net Core Web App(包括Web Api或者MVC Web App/Razer Web App),主要面向Web服务场景,需要对接Http trigger,我们称之为Http Invoke。下面分别介绍如何开发这两种场景的函数计算代码。

Normal Invoke开发

在函数计算中开发Normal Invoke非常简单,它包括一个Handler函数和一个可选的Init函数,我们支持static函数或者instance函数,以下只显示instance函数的签名。

Handler API 签名

OutputType func(InputType input) // 此处InputType和OutputType可以为Stream或者任何可序列化的对象。
OutputType func(InputType, IFcContext context) // 增加了IFcContext参数,可以获得函数计算相关信息(比如用来访问阿里云的临时AK等)
async Task<OutputType> func(InputType input)   // 当然,目前流行的Async style函数我们也一样支持,此处返回值也可以是async Task,下面也一样,省去不表。
async Task<OutputType> func(InputType input, IFcContext context)

从上面的签名可以看出,我们对于用户代码的限制是非常少的。如果不需要用到context的话完全不需要依赖任何函数计算的SDK,几乎就是free style编程。当然我们还是推荐用户采用带IFcContext的函数签名,除了能方便安全的访问阿里云资源之外,它自带的RequestId以及Logger对象等都是开发者诊断调试的好伙伴,下面是IFcContext定义,起内容和其他语言类似。

 public interface IFcContext
 {
        string RequestId { get; }

        IFunctionParameter FunctionParam {get;}

        IFcLogger Logger { get; }

        ICredentials Credentials {get;}

        string AccountId { get; }

        string Region { get; }

        IServiceMeta ServiceMeta { get; }
 }

在工程中添加Aliyun.Serverless.Core包即可使用该接口。

初始化 API

初始化API用来完成一些全局的初始化工作---也就是说该API只在容器服务的第一个请求时才被调用。

void Init(IFcContext context) //初始化函数不能有返回值,因为无法返回给用户。如果有返回值,那它也会被忽略

注意事项

  • 我们期望用户编写的函数是stateless的,基于这个前提和效率方面的考虑,Handler以及Init函数所在的对象在不同的请求之间是有可能被重用的。所以说如果Handler或者Init有改变对象成员变量的行为,则该函数运行时的结果可能受到上一次调用的影响。
  • 关于InputType以及OutputType,我们支持任何能被NewtonSoft.Json序列化的对象。如果该对象无法被NewtonSoft.Json序列化,则用户需要在类的定义上指定FcASerializes属性,以指定自定义的序列化方法,该自定义序列化实现需要随用户代码一起上传。
  • 初始化API和Handler API必须是public.

Http Invoke 开发

是的,用户可以在函数计算运行Asp.Net Core程序。将原有的Asp.Net Core程序改造成能被函数计算执行的代码也是非常简单的,在Nuget添加Aliyun.Serverless.Core.Http之后,只需要少量代码便可以移植到函数计算。

最简单的例子

假如下面是原来的Main函数---通过向导产生的代码

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

只需要在所在工程加入如下代码即可:

using System;
using Aliyun.Serverless.Core.Http;
using Microsoft.AspNetCore.Hosting;
namespace YourNameSpace
{
    public class FcRemoteEntrypoint : FcHttpEntrypoint
    {
        protected override void Init(IWebHostBuilder builder)
        {
            builder.UseStartup<Startup>();
        }
    }
}

移植一个简单的WebApp就这么简单。
稍微复杂点的例子,假如我们需要在Main函数里做一些初始化工作,如下代码所示:

public class Program
{
    public static void Main(string[] args)
    {
        Init1();
        IWebHost host = CreateWebHostBuilder(args).Build();
        Init2(host);
        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

此时FcRemoteEntryPoint则应该和Main一样,有Init1()以及Init2()的调用,如下:

using System;
using Aliyun.Serverless.Core.Http;
using Microsoft.AspNetCore.Hosting;
namespace YourNameSpace
{
    public class FcRemoteEntrypoint : FcHttpEntrypoint
    {
        protected override void Init(IWebHostBuilder builder)
        {
            Init1()
            builder.UseStartup<Startup>();
        }

        protected override void PostInit(IWebHost host)
        {
            Init2(host);
        }
    }
}

FcHttpEntrypoint剖析

这里FcHttpEntrypoint是FC提供在Aliyun.Serverless.Core.Http包里的基类,它起到两个作用:1) 调用用户的Asp.Net core初始化代码,作用类似于本地运行的Main函数。2)包含运行用户代码的入口函数,用来从函数计算执行引擎获得请求并传递给用户代码,代码执行完成后将用户响应返回给函数计算执行引擎。最简单的情形下用户只需要实现Init函数即可。下面详细介绍如何重载FcHttpEntrypoint中提供的方法来实现更为复杂的功能。

PostInit

如果需要对这个IWebHost对象在Init()之后、Start()之前做一些初始化操作(比如用DependencyInjection获得一些内部的Service做初始化),那应该重载PostInit函数。

protected virtual void PostInit(IWebHost host) { }

PostMarshallRequestFeature

如果需要对请求做一些额外的处理,可以在PostMarshallRequestFeature中进行

protected virtual void PostMarshallRequestFeature(IHttpRequestFeature aspNetCoreRequestFeature, HttpRequest request, IFcContext fcContext)

PostMarshallResponseFeature

如果需要对响应做一些额外的处理,可以在PostMarshallResponseFeature中进行

protected virtual void PostMarshallResponseFeature(IHttpResponseFeature aspNetCoreResponseFeature, HttpResponse response, IFcContext fcContext)

HandleRequest

HandleRequest是函数计算请求的入口函数,它将请求Mashall成Asp.net core的请求之后传给用户代码进行处理,再将用户代码返回的响应Marshall成函数计算响应返回给用户。在大部分情况下用户不需要重载这一函数。目前唯一需要重载的情况是如果用户设置自定义域名时还包括一个虚拟目录前缀(比如 http://abc.com/a/),由于函数计算的代码无法区分一个URL中哪个部分是虚拟目录,需要用户将request中的Path分成PathBase以及Path两部分,下面是示例代码

public override async Task<HttpResponse> HandleRequest(HttpRequest request, HttpResponse response, IFcContext fcContext)
{
    AdjustRequestPath(request); // set request.Path and request.PathBase correctly. User needs to implement it according to the virtual directory.
    return base.HandleRequest(request, response, fcContext);
}

注意事项

  • 在函数计算中,任何文件的写操作均应该指向NAS盘,本地文件由于各个container之间无法共享,因此无法保存程序的状态。
  • 如果Web工程依赖操作系统相关的native组件,用dotnet publish命令生成的publish文件夹下会产生runtimes目录。而FC默认的bin目录是代码所在目录或者代码所在目录的lib目录,因此需要手工将目录linux-x64/native下的.so文件复制到publish目录(或者publish/lib目录),然后再打包。
  • 目前函数计算要求Web App有自己的自定义域名,否则返回的内容将以附件形式返回,不方便调试。一个绕过的办法是用Chrome一个叫Undisposition的插件自动删除Content-Disposition:Attachment。

开发体验

下面以Normal Invoke为例开发一个简单OSS Event Handler。

创建空白工程

mkdir FcExample
cd FcExample
dotnet new console
或者
dotnet new classlib

添加FC类库引用

编辑FcExample.csproj,添加如下代码

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <AssemblyName>FcExample</AssemblyName>
  </PropertyGroup>
<ItemGroup>
    <PackageReference Include="Aliyun.Serverless.Core" Version="1.0.1" />
    <PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.9.1" />
    <PackageReference Include="Aliyun.Serverless.Core.Mock" Version="1.0.1" />
</ItemGroup>
</Project>

编写函数

编辑Program.cs,添加如下代码

using System;
using System.IO;
using Aliyun.OSS;
using Aliyun.Serverless.Core;
using Aliyun.Serverless.Core.Mock;
using Microsoft.Extensions.Logging;
namespace FcExample 
{
    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("req");
            }
            if (context == null || context.Credentials == null)
            {
                throw new ArgumentNullException("context");
            }

            context.Logger.LogInformation("GetOssFile started. {0}", context.Credentials.AccessKeyId);
            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;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string bucket = "<bucket name>";
            string key = "<key>";
            string id = "<id>";
            string secret = "<key>";
            string endpoint = "<oss endpoint>"; // http://oss-cn-hangzhou.aliyuncs.com
            string accountId = "FakeAccountId";
            string reqId = "RequestId123#" + DateTime.UtcNow;

            OSSFileHandler handler = new OSSFileHandler();
            FcContext context = new FcContext(accountId, reqId);
            Credentials credentials = new Credentials();
            credentials.AccessKeyId = id;
            credentials.AccessKeySecret = secret;
            context.Credentials = credentials;
            Stream stream = handler.GetOssFile(new OssFileHandlerRequest() { Bucket = bucket, Key=key, Endpoint= endpoint}, context);
            // verify stream
            stream.Close();
        }
    }

}

注意事项

  • 入口函数不能被overload--也就是说不允许在类中包含其他同名的函数。
  • 一般推荐本地测试函数在另一个工程中,在产品代码中不需要引用Aliyun.Serverless.Core.Mock,以减小代码包体积。
  • Http Invoke的函数是不能当做Normal Invoke来调用的,反之也亦然。

测试函数

在Main()中输入测试账号以及bucket、key信息,加入适当的检查stream的逻辑后,运行 dotnet run 即可测试

打包

运行dotnet publish -c Release,然后cd bin/Release/netcoreapp2.1/publish,最后zip code.zip *

注意事项

打包时确保所有依赖的dll文件在zip文件的根目录或者名为lib的子目录中,否则该依赖文件可能无法被加载

发布函数

去函数计算控制台创建一个dotnetcore2.1函数,上传code.zip,并指定函数Handler为FcExample::FcExample.OSSFileHandler::GetOssFile

小结

本文简单介绍了如何编写能运行在函数计算中的C#函数,包括normal invoke和Http invoke。如果有任何疑问和建议,欢迎留言。

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
6月前
|
Serverless 异构计算
函数计算FC部署问题之函数报错如何解决
函数计算FC部署是指在阿里云Function Compute服务上部署函数或应用程序的过程,它提供了一个无需管理服务器即可运行代码的平台;本合集将介绍和总结函数计算FC部署中常见的问题与解决方案,指导用户如何高效部署和管理Serverless函数。
|
监控 Serverless 测试技术
函数计算的开发
函数计算的开发
63 0
|
3月前
|
监控 Serverless 开发者
函数计算发布功能问题之查看函数的调用日志的问题如何解决
函数计算发布功能问题之查看函数的调用日志的问题如何解决
|
4月前
|
运维 Serverless 测试技术
函数计算产品使用问题之如何配置一个应用对应多个函数
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
4月前
|
运维 监控 JavaScript
函数计算产品使用问题之将自定义层(nodejs 20)添加到了函数中,本地运行没有问题,但在函数计算中出现问题,该怎么办
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
4月前
|
域名解析 缓存 前端开发
函数计算产品使用问题之添加的插件中只有部分显示成功,是什么导致的
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
4月前
|
存储 机器学习/深度学习 监控
函数计算产品使用问题之如何部署并使用已经替换的模型
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
4月前
|
存储 缓存 运维
函数计算产品使用问题之SD上安装了inpaint anything插件,但是不显示,该如何解决
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
359 0
|
5月前
|
运维 Serverless API
函数计算产品使用问题之如何开启函数计算FC的API模式
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
5月前
|
弹性计算 运维 监控
函数计算产品使用问题之如何修改函数计算中的应用代码,并同步跟新
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
下一篇
无影云桌面