WebP Cloud Services使用 Cloudflare Workers进行回源来保证原站服务器IP不泄漏。本着降本增效的理念,我们这次准备将部分回源请求迁移到 Azure Function。
Azure Function 同样也是由 Azure 提供的serverless服务,运行时可以使用 Python、NodeJS、Java和亲儿子 .NET、PowerShell,和 Cloudflare Workers一样,每个月前100万请求免费,具体信息可以参考价格表
我们的回源代码是使用 JavaScript写的,虽然这个语言挺扭曲的,但是为了方便迁移,就不变啦!
创建 Azure 账号
你需要有一个活跃订阅的Azure账号……
创建 Function
搜索栏搜索 Function App,点击 Create 按钮,会发现 Azure 提供了五种选项。选择第一个 Consumption
基本信息
进入配置页面,可以新建一个资源组,需要选择的是 Runtime,这里选择 nodejs,然后 Region选择离用户最接近的地方。这里和 Cloudflare Workers不一样,Cloudflare Workers是在全球部署的,会在离用户最近的节点执行;Azure 这里是在固定的节点执行,所以对于大陆用户来说,日韩港新是比较不错的位置。
存储
默认就行,要是看默认的名字不喜欢,可以自己改掉
网络
这里要选择Enable public access,要不然访问不了。
其他选项
Monitoring 想开就开,为了省钱可以关掉。当然后续也可以重新开启;
Deployment是配置CD的地方,可以以后再配置
部署
部署完成之后 Azure 会提供一个URL,打开之后应该是这样的
准备开发环境
不像 Cloudflare Workers 那么直接,开发 Azure Functions 最好用 VS Code。因为用 Azure Functions 扩展会很方便。
然后按F1弹出窗口 Azure Functions – Create New Project,语言就选择 JavaScript,愿意做类型体操也可以选择 TypeScript,或者任何你熟悉的语言都可以。
Trigger为如何触发Functions,这个场景下选择HTTP Trigger就可以了
熟悉开发环境
目录与文件
src/functions/fetchImage.js
这个就是你的代码host.json
:配置
部署
F1 – Deploy to Function App,选择订阅-刚刚创建的function
部署成功之后,浏览器访问:
https://blog-fun-post.azurewebsites.net/api/fetchimage
就可以看到 Hello World信息了。其中:
api
是路由前缀,可以自定义的fetchimage
是代码里写好的路由,当然也可以自定义,默认代码里定义的name
Azure 上可以看到已经部署好的代码
点进去终于有了和 Cloudflare Workers一样的在线编辑页面(不过可能是只读的,和选择的语言有关)
本地调试
每次改代码都部署一次确实不是好办法。这个时候就要用本地调试啦,由于我们是用 HTTP trigger的,调试起来非常简单方便。哦对了,调试 JavaScript 应用得有nodejs
打开 fetchImage.js
按下F5,这里选择 Connect Storage Account(其实选择 emulator绕过去也行,没关系)
如果是第一次运行,可能需要安装一些依赖,安装完成之后就会看到控制台输出了类似如下信息
Azure Functions Core Tools Core Tools Version: 4.0.6280 Commit hash: N/A +421f0144b42047aa289ce691dc6db4fc8b6143e6 (64-bit) Function Runtime Version: 4.834.3.22875 [2024-09-26T12:27:04.937Z] Debugger listening on ws://127.0.0.1:9229/882fb0c8-1957-4070-a6be-9cfd3250d77b [2024-09-26T12:27:04.938Z] For help, see: https://nodejs.org/en/docs/inspector [2024-09-26T12:27:05.003Z] Worker process started and initialized. [2024-09-26T12:27:05.058Z] Debugger attached. Functions: fetchImage: [GET,POST] http://localhost:7071/api/fetchImage For detailed output, run func with --verbose flag.
浏览器访问http://localhost:7071/api/fetchImag
e 就可以了,改了代码也会自动热加载,当然也可以打断点。
Functions
一个最简单的 Functions 代码是这样的
const { app } = require('@azure/functions'); app.http('fetch', { methods: ['GET', 'POST'], authLevel: 'anonymous', handler: async (request, context) => { context.log(`Http function processed request for url "${request.url}"`); return { body: `Hello hello hello` }; }, });
app.http
的第一个参数是名字,要唯一,并且默认是路由methods
表示接受的请求方法handler
是处理请求的地方,第一个参数是request
,可以用来获取请求体之类的信息,第二个是context
,执行上下文,包括日志功能、绑定数据、环境信息等。return
用于返回响应,按照文档context.res
也应该可以,但是在我这里不行,可能需要额外配置一下。返回一个object,编辑器会自动补全,比如body, status,headers 等
定义路由
路由前缀
默认,functions的代码都是 在 /api
这个路由下的,在 host.json
里可以进行配置
"extensions": { "http": { "routePrefix": "" } }
这样就没有 api
这个前缀了,当然你也可以随便改!
路由名称
默认路由名称是app.http
的第一个参数,如
app.http('fetch', { methods: ['GET', 'POST'], authLevel: 'anonymous', handler: handler, });
那么路由就是 fetch
,如果要自定义可以额外传入一个 route
。需要注意不可以是空字符串,也不可以是/
开头的
app.http('fetch', { methods: ['GET', 'POST'], authLevel: 'anonymous', handler: handler, route: "something" });
根访问
如果你希望应用在 https://example.com 直接访问,不需要/api
也不需要路由名称,那么就要综合以上两种方法,配置前缀,然后 route: "/"
不能留空😂,如果留空就会出现azure默认的页面
迁移代码
我们的Cloudflare Workers代码是这么写的,非常简单
async function handleProxy(post_body) { const headers = { "Accept": post_body.accept, "User-Agent": post_body.user_agent }; const response = await fetch(post_body.origin_url, { method: post_body.request_name, headers: headers }); if (response.ok) { const res = new Response(response.body, { status: response.status, statusText: response.statusText, headers: response.headers }); return res; } else { return new Response(response.statusText || "Unknown Error", { status: response.status, statusText: response.statusText }); } } export default { async fetch(request, env, ctx) { try { const post_body = await request.json(); return handleProxy(post_body); } catch (error) { return new Response("Invalid JSON data", { status: 400 }); } }, };
基本上要改的地方如下:
- 入口参数位置调换
- 改改函数名字之类的
也就是改一下那个default
async function handler(request, context) { try { const body = await request.json(); return handleProxy(body); } catch (error) { console.error('JSON parsing error:', error); return new Response('Invalid JSON data', { status: 400 }); } }
好了你的函数写好了,可以部署了。就是这么简单,除了配置环境那里麻烦点,别的都很容易,毕竟大家都是nodejs 20,自带了fetch
和Response
甚至可以通过运行时信息不同,使用不同的入口函数,进而做到用一套代码。
总结
- Function提供的语言比Cloudflare Workers多
- 大家都是JavaScript,迁移很简单。为了类型安全也可以使用 TypeScript
- Function是区域性部署;Workers是全球部署且就近执行
- Function 想要做到就近执行,可能得需要配合 FrontDoor + 多地部署,想想就头疼
- Function提供的计划挺多的
- Function也应该可以拿来做一些奇奇怪怪的事情
- Azure的线路很好,对大陆访问非常友好,而Cloudflare则是降速CDN了
- Azure的功能好多(价格也很美丽),还待以后慢慢学习尝试