CloudFlare员工获得Python、Scala、Kotlin、Reason和DART支持

2020-07-28 22:09:33

我们最初启动Cloudflare Workers时支持JavaScript和编译成WebAssembly的语言,如Rust、C和C++。从那时起,Cloudflare和社区改进了工人打字稿的可用性。但是我们还没有过多地谈论编译成JavaScript的许多其他流行语言。今天,我们很兴奋地宣布支持Python、Scala、Kotlin、Reason和Dart。

入门非常简单,只需安装Wrangler,然后为您选择的语言(Python、Scala、Kotlin、Dart或Reason)的模板运行Generate即可。对于Python,这看起来如下所示:

按照生成的项目目录内自述文件中的安装说明进行操作,然后运行wrangler Publish。您可以在workers.dev子域查看工作人员的输出,例如https://my-python-project.cody.workers.dev/.。如果您还没有免费工人帐户,您可以注册一个。

就这样。用你最喜欢的语言编写真的很容易。但是,如果我们就这样不说了,这篇博文就不会很有说服力了。现在,我将把重点转移到我们如何添加对这些语言的支持以及如何添加对其他语言的支持。

语言功能很重要。例如,一旦你使用了模式匹配,你就很难放弃它的安全性和表现力。熟悉的语法对我们程序员来说很重要。

您可能还拥有您想要重用的首选语言的现有代码。请记住,在V8上运行的优势有一个限制,即如果您使用依赖于本机代码或特定于语言的VM特性的库,它们可能无法转换为JavaScript。在这种情况下,WebAssembly可能是一种选择。但是对于内存管理的语言,您通常最好编译成JavaScript,至少在关于WebAssembly垃圾收集的故事稳定下来之前是这样。

我将介绍如何使用动态类型语言Python和静态类型语言Scala的代表性示例创建Worker语言模板。如果您想要继续操作,您需要安装Wrangler并使用您的工人帐户进行配置。如果这是你第一次使用Workers,那么快速入门是个好主意。

Wrangler将创建一个my-python-project目录,并提醒您在其中的wrangler.toml文件中配置您的account_id。目录中的readme.md文件链接到我们正在使用的Python到JavaScript编译器Transcrypt的设置说明。如果您已经安装了Python 3.7和viralenv,则只需运行

在Worker上编译为JavaScript的主要要求是能够生成符合我们的捆绑包大小限制为1MB的单个js文件。在本例中,Transcrypt为其Python运行时增加了大约70k,这完全在这个限制之内。但默认情况下,在Python文件上运行transcrypt将在__target__目录中生成多个JS和源映射文件。谢天谢地,牧马人已经建立了对webpack的支持。有一个webpack加载器用于转码,使它很容易产生一个单一的文件。有关设置,请参阅webpack.config.js文件。

所有这些都是为了运行一些Python代码,所以让我们来看看index.py:

Def handleRequest(Request):return__new__(Response(';Python worker hello world!';,{';Header';:{';Content-Type';:';Text/Plain';}}))addEventListener(';FETCH';,(λ事件:event.respondWith(handleRequest(event.request))))。

在大多数方面,这与任何其他工作者的hello world非常相似,只是在Python语法上。字典文字取代了JavaScript对象,使用lambda代替了匿名箭头函数,等等。如果使用__new__创建JavaScript类的实例看起来有点笨拙,那么代码转换文档将讨论一种替代方案。

显然,addEventListener不是一个内置的Python函数,它是Workers运行时的一部分。因为Python是动态类型的,所以您不必担心为JavaScriptAPI提供类型签名。缺点是,错误会在您的Worker运行时导致失败,而不是在Transcrypt编译您的代码时。代码转换确实支持使用mypy进行某种程度的静态检查。

我们正在使用的Scala到JavaScript编译器是Scala.js。它有一个用于Scala构建工具的插件,因此只需安装SBT和JDK即可。

在项目目录中运行SBT fullOptJS会将Scala代码编译成单个index.js文件。将build.sbt中的构建配置设置为输出到项目的根目录,Wrangler希望在那里找到index.js文件。之后,您可以照常运行Wrangler Publish。

Js在运行fullOptJS时使用Google闭包编译器优化大小。对于hello world,文件大小为14k。一个更现实的项目涉及异步抓取,重量在100k左右,仍然在工人的限制之内。

为了利用静态类型检查,您将需要您使用的JavaScriptAPI的类型签名。现有的Scala签名用于FETCH和服务工作者相关的API。您可以在Worker Main.scala的入口点中看到正在导入的文件:

通过导入scala.scalajs.js,可以轻松访问与JavaScript类型等效的Scala,比如js.Array或js.Dictionary。Main的其余部分看起来非常类似于TypeScript Worker hello world,但存在语法差异,例如类型参数的Unit代替void和方括号代替尖括号:

Object Main{def Main(args:Array[String]):Unit={Globals.addEventListener(";Fetch";,(Event:FetchEvent)=>;{event.respondWith(handleRequest(event.request))})}def handleRequest(Request:Request):Response={new Response(";Scala worker hello world";,ResponseInit(_Headers=js.Dictionary(";Content-Type";

Request、Response和FetchEvent由前面提到的导入定义。但是这个Globals对象是什么呢?JavaScriptAPI有一些特定于Worker的扩展。您可以在静态类型语言中处理这些问题,方法是自动转换Worker的现有TypeScript类型定义或为要使用的功能编写类型签名。编写类型签名并不难,知道如何做是件好事,所以我在Globals.scala中添加了一个示例:

Import scalajs.jsimport [email protected]@JSGlobalScopeobject Globals扩展js.Object{def addEventListener(`type`:string,f:js.Function):unit=js.ative}。

注释@js.ative表明该实现是在现有JavaScript代码中实现的,而不是在Scala中实现的。这就是addEventListener定义的主体只是js.ative的原因。在JavaScript工作器中,您可以调用addEventListener作为全局范围内的顶级函数。这里,@JSGlobalScope注释指示我们重新定义的函数签名在JavaScript全局作用域中可用。

您可能注意到,传递给addEventListener的函数类型只是js.Function,而不是指定参数和返回类型。如果您想要更多的类型安全,这可以作为js.Function1[FetchEvent,Unit]来实现。如果您试图以牺牲安全为代价快速工作,您可以使用def addEventListener(any:any*):any来允许任何操作。

让我们来看一个更现实的使用Worker KV和异步调用的示例。该项目的想法是使用我们自己的HTTP API来存储和检索文本值。为简单起见,我使用路径的第一个斜杠分隔的部分作为键,使用第二个斜杠分隔的部分作为值。已完成项目的使用将看起来像PUT/生命意义/42或GET/生命意义/

我需要做的第一件事是在Globals.scala中为我正在使用的KV API的部分添加类型签名。我在wrangler.toml中的KV命名空间绑定将被命名为KV,从而产生一个对应的全局对象:

Object Globals扩展js.Object{def addEventListener(`type`:string,f:js.Function):unit=js.ative Val KV:KVNamespace=js.ative}。

Bash$curl-w&34;\n&34;-X放置';https://scala-kv-example.cody.workers.dev/meaning of Life/42';bash$curl-w";\n&34;-X Get';https://scala-kv-example.cody.workers.dev/meaning of Life/';42。

那么KVNamespace类型的定义是什么呢?它是一个接口,因此它成为带有@js.ative注释的Scala特征。我现在需要添加的唯一方法是接受和返回字符串的KV.get和KV.put的简单版本。返回值是异步的,因此它们被包装在js.promise中。我将使包装的字符串成为类型别名KVValue,以防我们将来要处理数组或流返回类型:

Object KVNamespace{type KVValue=js.Promise[String]}@js.nativetrait KVNamespace扩展js.Object{import KVNamespace._def get(key:string):KVValue=js.ative def put(key:string,value:string):js.Promise[Unit]=js.ative}。

类型签名完成后,我将继续讨论Main.scala以及如何处理与JavaScript承诺的交互。可以直接使用js.Promise,但我更喜欢使用异步期货的Scala语义。J.JSConverter中的方法toJSPromise和toFuture可用于来回转换:

Def get(key:string):Future[Response]={Globals.KV.get(Key).toFuture.map{(value:string)=>;new response(value,okInit)}recover{case err=>;new response(s";获取';$key';:$err";,errInit)}}。

放置值的函数类似于使用toFuture将KV的返回值转换为Future。我使用MAP将值转换为响应,并恢复以处理故障。如果您更喜欢异步/等待语法,而不是使用组合符,您可以使用scala-async。

最后,handleRequest的新定义是一个很好的例子,说明了模式匹配如何同时使代码更简洁、更不容易出错。我们精确匹配所需的HTTP方法和路径组件的组合,对于任何其他情况,缺省为信息性错误:

Def handleRequest(request:request):Future[Response]={(request.method,request.url.plit(";/";))Match{case(HttpMethod.GET,Array(_,key))=>;get(Key)case(HttpMethod.PUT,Array(_,key,value))=>;put(key,value)case_=>。,errInit))}}。

我是编程语言的粉丝,并将继续添加更多的Worker模板。您可能比我更了解您最喜欢的语言,所以对于简单的hello world或更复杂的示例,拉请求都是受欢迎的。

如果你对编程语言感兴趣,请查看RedMonk的最新语言排名,Python是有史以来第一个进入这些排名前两名的非Java或JavaScript语言。

CloudFlare工作人员无服务器周JavaScript Python