函数计算新功能-----支持C#函数-阿里云开发者社区

开发者社区> tonyxx> 正文

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

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
函数式编程初探
函数式编程初探  (原文地址:http://blog.jobbole.com/17228/) 04月 11, 2012 at 9:50 am by 齐哲 Tags: Erlang, 函数式编程, 编程语言   诞生50多年之后,函数式编程(functional programming)开始获得越来越多的关注。   不仅最古老的函数式语言Li
1143 0
2014秋C++ 第12周项目 C++函数新特征与递归函数
课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703,课程资源在云学堂“贺老师课堂”同步展示,使用的帐号请到课程主页中查看。  【项目1- 阅读程序】阅读下列程序,写出程序的运行结果。上机时运行程序,与你的预期进行对照、理解。如果对运行结果和其背后的原理仍不理解,请通过单步执行的手段跟踪理解。(1)阅读下面两个有静态局部变量
1133 0
C++函数对象
  原文:http://blog.csdn.net/ggggqqqqihc/article/details/1727020   标准库里的count_if可以统计容器中满足特定条件的元素的个数。例如要统计一个整数vector——ivec中正数的个数,可以先写一个返回类型为bool,含有一个int参数的条件函数: bool pred(int val){ return val>0; }    之后可以用count_if(ivec.begin(),ivec.end(),pred)计算出正整数的个数。
484 0
新功能初探 | RDS MySQL 8.0 支持 DML 语句 returning
MySQL 对于 statement 执行结果报文通常分为两类 Resultset 和 OK/ERR,针对 DML 语句则返回OK/ERR 报文,其中包括几个影响记录,扫描记录等属性。
6906 0
请问:hive中avg聚合函数会使用到combiner功能吗?
hive avg函数是否可以使用combiner功能
2900 0
函数计算搬站新利器---Custom Runtime演示
功能介绍 今天在函数计算上实现一个Web站点是比较简单的,比如: 部署基于 python wsgi web 框架的工程到函数计算 介绍的用WSGI框架结合函数计算部署Python的Web App。最近函数计算发布了一个新功能,使得搬站进一步简化了。
2286 0
+关注
tonyxx
OSS开发团队
3
文章
0
问答
来源圈子
更多
阿里云存储基于飞天盘古2.0分布式存储系统,产品包括对象存储OSS、块存储Block Storage、共享文件存储NAS、表格存储、日志存储与分析、归档存储及混合云存储等,充分满足用户数据存储和迁移上云需求,连续三年跻身全球云存储魔力象限四强。
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载