当前位置: 首页 > news >正文

EdgeOne 边缘函数 - 构建边缘网关

目前,各大主流厂商都推出了自己的边缘 Serverless 服务,如 CloudFlare Workers、 Vercel EdgeRuntime 等;腾讯云 EdgeOne 边缘函数提供了部署在边缘节点的 Serverless 代码执行环境,只需编写业务函数代码并设置触发规则,即可在靠近用户的边缘节点上弹性、安全地运行代码。

一、前言

在许多业务场景中,需要对请求或响应进行处理,以实现特定的功能或优化用户体验。通常,这些操作在网关层完成。使用 EdgeOne 边缘函数实现请求改写或重定向,构建边缘网关,可以帮助开发者可以更加灵活地控制请求流程,同时减轻网关服务的负担。

二、边缘网关

使用 EdgeOne 边缘函数构建轻量边缘网关服务,有如下优势:

  1. 全球网络:边缘网关可以在用户地理位置附近处理请求,减少延迟,提高响应速度。

  2. 高性能:边缘函数基于 V8 引擎打造,几乎没有冷启动延迟。

  3. 成本效益:高效利用边缘算力,可以有效降低业务成本。

  4. 服务集成:在边缘网关层面集成第三方服务,如身份验证、自定义缓存策略等,无需修改前后端服务。

  5. 零维护:EdgeOne 负责维护底层基础设施,开发者可以专注于编写和代码,不必担心服务器的维护和运维。

  6. 即时更新:代码更新可以在全球范围内快速生效。

三、代码示例

1. 代码示例旨在展示边缘函数的能力,仅列举常见方案的基本实现;
2. 文章中涉及的代码可私信获取;

3.1 请求重定向

请求重定向在实际业务场景中是一个常见的操作,例如:网站或服务正在进行维护,需要将请求重定向到一个维护通知页面。仅需几行代码,便可实现边缘重定向。

3.1.1 Fetch Redirect

使用 fetch 第三方域名的方式,实现隐式重定向:

const REDIRECT = 'https://tencent.com/';async function handleEvent(event) {const { request } = event;const url = new URL(request.url);// 直接 fetch 第三方域名const res = await fetch(`${REDIRECT}${url.pathname}${url.search}`, request);event.respondWith(res);
}addEventListener('fetch', handleEvent);

localhost:8080 隐式重定向到 https://tencent.com

3.1.2 302 Redirect

还可以使用 302 的方式,实现显式重定向:

const REDIRECT = 'https://www.tencentcloud.com/';async function handleEvent(event) {const url = new URL(event.request.url);if (url.pathname === '/') {// 返回 302 响应event.respondWith(Response.redirect(REDIRECT));}return;
}addEventListener('fetch', handleEvent);

注意:
通常情况下,站点级别的重定向使用 302 形式,接口级别的重定向使用  fetch 的形式,根据实际情况选择不同的重定向方式;

3.2 日志上报

使用边缘函数,拦截请求/响应,将指定数据上报到 腾讯云 CLS 日志服务平台:

import { AsyncClient, Content, LogGroup, LogItem, PutLogsRequest } from '@tencent/edgefunction-cls-sdk';/** CONFIG START */const TENCENT_SECRET_ID = 'xxxxx';
const TENCENT_SECRET_KEY = 'xxxxx';const CLS_END_POINT = 'xxxxx';
const CLS_TOPIC_ID = 'xxxxx';/** CONFIG END */const clsAsyncClient = new AsyncClient({endpoint: CLS_END_POINT,secretId: TENCENT_SECRET_ID,secretKey: TENCENT_SECRET_KEY,....
});async function clsUpload(data) {...const logContent = new Content('__CONTENT__', JSON.stringify(data));...const clsRequest = new PutLogsRequest(CLS_TOPIC_ID, logGroup);const clsResponse = await clsAsyncClient.PutLogs(clsRequest);...
}async function doClsUpload(request) {try {const data = await request.clone().text();await clsUpload(data);} catch (err) {...}
}async function handleRequest(event) {const { request } = event;const contentType = request.headers.get('content-type') || '';if (contentType.includes('application/json') || contentType.includes('text/plain')) {// 用于通知边缘函数等待 Promise 完成,可延长事件处理的生命周期,不阻塞 fetch(request) 的返回。event.waitUntil(doClsUpload(request));}return;
}addEventListener('fetch', handleRequest);

上面的代码使用 event.waitUntil() 实现了在不阻塞响应的情前提下,将请求体中携带的数据上报到 腾讯云 CLS 日志服务,,可以在 腾讯云控制台 - 日志服务 - 检索分析 查看:

3.3 性能监控

在边缘函数中,通过修改响应体的方式,插入 腾讯云 RUM 前端性能监控 相关代码,统一处理页面监控的相关逻辑,简化操作流程:

...
async function handleEvent(event) {const { request } = event;const response = await fetch(request);if (response.status !== 200 || response.headers.get('Content-Type') !== 'text/html') {return response;}const aegisScript = `<script src="https://tam.cdn-go.cn/aegis-sdk/latest/aegis.min.js"></script>
<script>const aegis = new Aegis({id: '${AEGIS_ID}',...});
</script>
`;let resBody = await response.clone().text();resBody = resBody.replace('</title>', `</title>\n${aegisScript}`);const modifiedRes = new Response(resBody, response);event.respondWith(modifiedRes);
}addEventListener('fetch', handleEvent);

源站响应的 HTML 文件被注入了 aegis SDK 的相关代码:

性能监控相关数据可以在 腾讯云控制台 - 前端性能监控 - 页面性能 查看:

3.4 i18n 国际化

我们可以在边缘函数函数中,根据客户所在国家和地区,以及该地区使用的语言,实现站点国际化。

该功能可以使用  ef-flow-sdk 实现。
...function getOfficialLanguages(countryCodeAlpha2) {const territoryInfo = cldr.supplemental.territoryInfo?.[countryCodeAlpha2];if (territoryInfo) {const languages = territoryInfo.languagePopulation;const langs = Object.keys(languages).filter(lang => languages[lang]._officialStatus === 'official').map(lang => {const qValue = languages[lang]._populationPercent / 100;return `${lang};q=${qValue.toFixed(2)}`;});return langs;}return [];
}export function generateAccLangByGeo(request) {const { countryCodeAlpha2 } = request?.eo?.geo || {};let acceptLanguage = null;if (countryCodeAlpha2) {const officialLangs = getOfficialLanguages(countryCodeAlpha2).join(',');acceptLanguage = officialLangs || '';}// 如果没有成功匹配(没有命中规则 / 没有 geo),则生成一个默认头部 enreturn acceptLanguage || 'en';
}const langConfig = {origin: {title: 'CSR - REACT',},localization: [{lang: 'zh',title: '中文标题',},],
};async function handleEvent(event) {const { request } = event;if (new URL(request.url).pathname !== '/') {return;}const response = await fetch(request);if (response.status !== 200 || response.headers.get('Content-Type') !== 'text/html') {return event.respondWith(response);}const originRes = response.clone();const acceptLanguage = request.headers.get('Accept-Language') || generateAccLangByGeo(request);const parsedAccLang = parser.parse(acceptLanguage);let hit = null;for (const acclang of parsedAccLang) {const { code, region } = acclang;const rule = langConfig.localization.find(rule => {const parsedRule = rule.lang.split('-');if (parsedRule[1]) {return parsedRule[0] === code && parsedRule[1] === region;}return parsedRule[0] === code;});if (code && rule) {hit = rule;break;}}if (hit) {let retBody = await response.text();retBody = retBody.replaceAll(new RegExp(langConfig.origin.title, 'ig'), hit.title);return event.respondWith(new Response(retBody, originRes));}return;
}addEventListener('fetch', handleEvent);

3.5 地域封禁

在边缘函数中,可以基于 GEO 信息,实现地理位置的封禁。

// 国家封禁列表 伊朗、古巴、朝鲜、叙利亚
const CLOSURE_COUNTRY_CODE_LIST = ['IR', 'CU', 'KP', 'SY'];
// 地区封禁列表 克里米亚地区 UA-43、顿巴斯地区 (顿涅茨克 UA-14、卢甘斯克 UA-09)
const CLOSURE_REGION_CODE_LIST = ['UA-14', 'UA-09', 'UA-43'];function checkClosureGeo(request) {const { countryCodeAlpha2, regionCode } = request?.eo?.geo || {};// 判断 countryCodeif (countryCodeAlpha2 && CLOSURE_COUNTRY_CODE_LIST.includes(countryCodeAlpha2)) {return true;}// 判断 regionCodeif (regionCode && CLOSURE_REGION_CODE_LIST.includes(regionCode)) {return true;}return false;
}function handleEvent(event) {if (checkClosureGeo(event.request)) {event.respondWith(new Response('Forbidden', { status: 403 }));}return;
}addEventListener('fetch', event => {event.passThroughOnException();handleEvent(event);
});
http://www.xdnf.cn/news/182161.html

相关文章:

  • 【AI提示词】领导力教练
  • JavaScript性能优化实战:从瓶颈定位到极致提速
  • Spark 技术体系深度总结
  • 常用的ADB命令分类汇总
  • markdown-it-katex 安装和配置指南
  • Leetcode刷题记录20——找到字符串中所有字母异位词
  • Java高频面试之并发编程-09
  • 大模型高效背后的反思
  • 检测软件系统如何确保稳定运行并剖析本次检测报告?
  • springboot当中的类加载器
  • Opnelayers:向某个方向平移指定的距离
  • 7.14 GitHub命令行工具测试实战:从参数解析到异常处理的全链路测试方案
  • 视觉导航中的回环检测技术解析
  • Gentex EDI 需求分析
  • 封装成帧的学习
  • 软考-软件设计师中级备考 2、计算机系统组成、指令系统
  • 【JavaScript】二十七、用户注册、登陆、登出
  • Ldap高效数据同步- MirrorMode双主复制模式配置详解(上)
  • 【KWDB创作者计划】_企业级多模数据库实战:用KWDB实现时序+关系数据毫秒级融合(附代码、性能优化与架构图)
  • OpenGl ES 3.0 笔记一:初步概念
  • 4.27 JavaScript核心语法+事件监听
  • 小球在摆线上下落的物理过程MATLAB代码
  • NL2SQL调研
  • 抗体品牌及产品介绍
  • 【三大特性】对象模型
  • 前端开发资源缓存策略
  • 全球城市范围30米分辨率土地覆盖数据(1985-2020)
  • 信奥赛之c++基础(常用数学函数)
  • 显存在哪里看 分享查看及优化方法
  • Milvus如何实现关键词过滤和向量检索的混合检索