让你的 CloudFlare Workers 代码无缝兼容 Deno、Vercel 和 NodeJS

有的时候我们的需求,仅仅需要写一些很小的脚本,甚至不用考虑路由、第三方库。这种时候,Serverless函数就很符合我们的要求。甚至 Deno.land 和 CloudFlare Workers 还可以在线编写,一键部署,再合适不过了。

很多厂商提供Serverless函数供白嫖,如下表:

厂商 额度
CloudFlare Workers 10W请求/天
Vercel Edge Function 50W请求/月
Deno Land 100W请求/月
NodeJS/Deno 本地部署 无限制

然而,这些平台虽然有不少的相似之处,却也有一些差异,后续怎么集成到本地运行也存在问题。
但他们这些云函数都有一个共性,就是导出一个请求参数为Request返回值为Response的函数。
那我们只要用胶水语言做一些微小的操作,即可轻松让我们的代码同时兼容 Workers ,Deno,Vercel 和 NodeJS 。

下面的代码,只要在 handler 函数内实现你的逻辑,即可同时兼容 Workers
,Deno,Vercel 和 NodeJS

async function handler(request) {
  return new Response("Hello World");
}

(async () => {
  if (typeof Deno !== 'undefined') {
    //For Deno
    console.log(`Listening on http://localhost:${Deno.env.get("PORT") || 8000}`)
    return Deno.serve({ port: Deno.env.get("PORT") || 8000 }, handler);
  }
  if (typeof EdgeRuntime !== 'undefined') {
    //For vercel edge serverless
    return
  }
  if (typeof addEventListener === "function") {
    //For Cloudflare Workers
    return
  }
  //For Nodejs
  const { serve } = await eval("import('@hono/node-server')");
  console.log(`Listening on http://localhost:${process.env.PORT || 8000}`)
  serve({
    fetch: handler,
    port: process.env.PORT || 8000,
  })
})()


//For vercel edge serverless - START
export const config = {
  runtime: 'edge',
  regions: ['hkg1'],
}

export const GET = handler
export const POST = handler
export const PUT = handler
export const PATCH = handler
export const DELETE = handler
export const HEAD = handler
export const OPTIONS = handler
//For vercel edge serverless - END

//For Cloudflare Pages Function - START
export function onRequest(context) {
  return exports.fetch(context.request)
}
//For Cloudflare Pages Function - END

//For Cloudflare Workers Function - START
export default {
  fetch(req, env, ctx) {
    return handler(req);
  }
}
//For Cloudflare Workers Function - END

为了使我们的代码尽可能地兼容不同的runtime,代码编写请遵守以下规范:

  1. 不使用第三方库,使用Fetch进行网络请求
  2. 不能使用Nodejs的流,可以使用const { readable, writable } = new TransformStream(); 操作流,使用 TextEncoder、TextDecoder解码流数据。
  3. Edge Runtime 这个链接的API支持可以作为参考。

这种Edge Serverless的开发,较Nodejs开发有一点限制。但是对于只需要实现简单功能的小工具来说,够用了。重要的是,可以很方便地在线编写,也可以使Serverless代码在几个云平台和本地之间直接迁移。

打个样,以下代码在 Workers ,Deno,Vercel 和 NodeJS 上可以无缝运行:

async function handler(request) {
  if (request.method === "OPTIONS") {
    return new Response(null, {
      headers: {
        'Access-Control-Allow-Origin': '*',
        "Access-Control-Allow-Headers": '*'
      }, status: 204
    })
  }

  var msg = decodeURIComponent("%E5%95%8A%E5%95%8A%E5%95%8A%EF%BC%8C%E5%A5%BD%E7%97%9B%EF%BC%8C%E5%93%A5%E5%93%A5%E6%8F%92%E5%BE%97%E5%A4%AA%E6%B7%B1%E4%BA%86%EF%BC%81%EF%BC%81%EF%BC%81%EF%BC%81%EF%BC%81%EF%BC%81%EF%BC%81%EF%BC%81%EF%BC%81");

  const { readable, writable } = new TransformStream();
  const my_stream_writer = writable.getWriter();
  var encoder = new TextEncoder();
  ; (async () => {
    for (let i = 0; i < 10; i++) {
      for (let j = 0; j < msg.length; j++) {
        await sleep(20)
        var txt = JSON.stringify({ "id": "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", "object": "chat.completion.chunk", "created": null, "model": "gpt-3.5-turbo", "choices": [{ "index": 0, "delta": { "role": "assistant", "content": msg[j] }, "finish_reason": null }] })
        await my_stream_writer.write(encoder.encode('data: ' + txt + '\n\n'));
      }
    }
    await my_stream_writer.write(encoder.encode(`data: {"id":"chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK","object":"chat.completion.chunk","created":null,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}\n\n`));
    await my_stream_writer.close();

  })()
  return new Response(readable, {
    headers: {
      'Access-Control-Allow-Origin': '*',
      "Access-Control-Allow-Headers": '*',
      'Content-Type': 'text/event-stream; charset=UTF-8'
    }, status: 200
  })

}
function sleep(ms) {
  return new Promise((r) => {
    setTimeout(r, ms);
  })
}


(async () => {
  if (typeof Deno !== 'undefined') {
    //For Deno
    console.log(`Listening on http://localhost:${Deno.env.get("PORT") || 8000}`)
    return Deno.serve({ port: Deno.env.get("PORT") || 8000 }, handler);
  }
  if (typeof EdgeRuntime !== 'undefined') {
    //For vercel edge serverless
    return
  }
  if (typeof addEventListener === "function") {
    //For Cloudflare Workers
    return
  }
  //For Nodejs
  const { serve } = await eval("import('@hono/node-server')");
  console.log(`Listening on http://localhost:${process.env.PORT || 8000}`)
  serve({
    fetch: handler,
    port: process.env.PORT || 8000,
  })
})()


//For vercel edge serverless - START
export const config = {
  runtime: 'edge',
  regions: ['hkg1'],
}

export const GET = handler
export const POST = handler
export const PUT = handler
export const PATCH = handler
export const DELETE = handler
export const HEAD = handler
export const OPTIONS = handler
//For vercel edge serverless - END

//For Cloudflare Pages Function - START
export function onRequest(context) {
  return exports.fetch(context.request)
}
//For Cloudflare Pages Function - END

//For Cloudflare Workers Function - START
export default {
  fetch(req, env, ctx) {
    return handler(req);
  }
}
//For Cloudflare Workers Function - END

mixed.zip (4.4 KB)

KV还没有封装,您可按照自己的使用习惯,自行实现。(CloudFlare Workers 、Deno 、和 Vercel 均支持 KV)

41 个赞

佬! 威武啊!

5 个赞

过奖过奖,互相学习 :smiling_face_with_three_hearts: :smiling_face_with_three_hearts:

1 个赞

:hugs:学习了

2 个赞

感谢大佬分享

2 个赞

插眼

3 个赞

好东西啊

2 个赞

向佬学习~ :smiling_face_with_three_hearts:

3 个赞

mark

2 个赞

强啊!
佬!

2 个赞

学习了

2 个赞

就喜欢楼主这样的科普文章:+1:

2 个赞

点赞,mark

2 个赞

mark

2 个赞

没学过,不会 感觉很牛的样子

2 个赞

太强了!

1 个赞

我记得有框架,可以支持多服务供应商的函数服务
GitHub - serverless/serverless: ⚡ Serverless Framework – Use AWS Lambda and other managed cloud services to build apps that auto-scale, cost nothing when idle, and boast radically low maintenance. 这个配合插件

2 个赞

感觉你说的是:

1 个赞

前来学习

From #dev to 开发调优