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

本文涉及的产品
简介: 函数计算作为新兴的事件驱动serverless平台正受到越来越多开发者的欢迎,之前已支持Java, Python, Nodejs, Php四种语言。现在函数计算正式支持C#。

声明

本文转载自 原文

前言

函数计算作为新兴的事件驱动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。如果有任何疑问和建议,欢迎留言。

相关实践学习
基于函数计算一键部署掌上游戏机
本场景介绍如何使用阿里云计算服务命令快速搭建一个掌上游戏机。
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
6天前
|
人工智能 关系型数据库 Serverless
Serverless 应用引擎常见问题之生成的函数镜像改为自定义的镜像如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
21 5
|
2月前
|
JSON 安全 Serverless
在使用阿里云函数计算(FC)服务时,您可以通过自定义域名来访问部署好的云函数
在使用阿里云函数计算(FC)服务时,您可以通过自定义域名来访问部署好的云函数【1月更文挑战第23天】【1月更文挑战第112篇】
212 7
|
2月前
|
Serverless 异构计算
函数计算FC部署问题之函数报错如何解决
函数计算FC部署是指在阿里云Function Compute服务上部署函数或应用程序的过程,它提供了一个无需管理服务器即可运行代码的平台;本合集将介绍和总结函数计算FC部署中常见的问题与解决方案,指导用户如何高效部署和管理Serverless函数。
|
6天前
|
人工智能 关系型数据库 Serverless
Serverless 应用引擎常见问题之API生成的函数镜像改为自定义的镜像如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
23 3
|
15天前
|
JavaScript 前端开发 Serverless
函数计算新功能— 支持 Node.js 18 、Node.js 20 运行时
从2024年2月起,函数计算正式发布 Node.js 18 运行时和 Nodejs.20 运行时,函数计算2.0和函数计算3.0都支持新的运行时,目前新运行时处在公测状态,欢迎大家来体验。
436 0
|
5月前
|
Serverless API 数据库
在阿里函数计算中,国内的云函数,海外调用有什么限制么?
在阿里函数计算中,国内的云函数,海外调用有什么限制么?
48 1
|
2月前
|
存储 监控 Serverless
在处理阿里云函数计算3.0版本的函数时,如果遇到报错但没有日志信息的情况
在处理阿里云函数计算3.0版本的函数时,如果遇到报错但没有日志信息的情况【1月更文挑战第23天】【1月更文挑战第114篇】
59 5
|
3月前
|
Serverless Python
在函数计算中,如果云函数的返回结果包含了`Content-Disposition: attachment`头部在函数计算中
在函数计算中,如果云函数的返回结果包含了`Content-Disposition: attachment`头部在函数计算中
132 2
|
7月前
|
运维 数据挖掘 测试技术
函数性能探测:更简单高效的 Serverless 规格选型方案
函数性能探测:更简单高效的 Serverless 规格选型方案
|
9月前
|
Serverless
在函数计算中实现定时触发自定义环境中的 HTTP 函数
在函数计算中实现定时触发自定义环境中的 HTTP 函数
59 0

热门文章

最新文章

相关产品

  • 函数计算