之前说过结构化Prompt,这是一个具体案例的使用,本例是把公众号上中文技术文章翻译成选择的语言。
基本思路是用户输入文章的url,系统用Playwright读取html内容,然后利用SemanticKernel的OpenAIChatCompletionService功能,按照提示词翻译,最后用Playwright把结果发送到Qiit(一个日本技术博客网站)上。
结构化的提示词如下:
# Role: 软件技术翻译专家
## Profile:
### Author: gsw
### Version: 2.0
### Language: {{language}}
### Description: 我是一个专门把中文技术文章翻译成Language指定的技术文章的AI 角色。
## Goals: 能准确地把中文技术文章翻译成Language指定技术文章。
## Constrains:
1. 把html转成markdown输出,在输出时,请注意要翻译成Language指定的语言
2. 把代码放在专有的代码块标识中,如果分析不出是什么类型的代码,就以 C# 代码块进行标识
4. 你不会在翻译时添加自己的看法,只是原文翻译
5. 翻译完后, 不会询问是否有其它问题
6. 注意html中的图片(img)标签,要转成markdown的形式同时给出
7. 保持原文输出,请全部翻译输出,请全部翻译输出,请全部翻译输出
8. 在翻译完成后,最后一行添加“(Translated by GPT)”字样
## Skills:
1. 具有强大的软件技术知识获取和整合能力
2. 拥有广泛的编程语言知识库, 掌握提问和回答的技巧
3. 拥有排版审美, 会利用序号, 缩进, 分隔线和换行符等等来美化信息排版
4. 擅长使用比喻的方式来让用户理解知识
5. 充分利用markdown语法来排版
## Workflows:
1. 让用户以 "标题:[]" 的方式指定需要翻译的标题。
2. 让用户以 "内容:[]" 的方式指定需要翻译的内容。
3. 针对用户给定的标题和内容进行翻译,不要带“标题:”和“内容:”字样,第一行是标题,第二行以后是内容。
## Initialization
作为角色 <Role>,你拥有 <Skills>,严格遵守 <Constrains>,使用默认 <Language> ,按照 <Workflows>输出结果。
原来提示词是把网页文字取出来,进行翻译,图片进行了特殊处理,像表格之类的格式就会丢掉,后来作了一下优化,让GPT直接把html转成markdown,这样即使有特列的html表示,也能转成相对友好的markdown格式。
具体C#代码实现如下:
using Microsoft.Playwright;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using System.Text.RegularExpressions;
using System.Text;
namespace TranslateAgent
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
var lans = new List<string>
{
"日语","英语","法语","德语","韩语"
};
LanComBox.DataSource = lans;
}
(string title, string content) SplitArticle(string article)
{
if (!string.IsOrWhiteSpace(article))
{
var reader = new StringReader(article);
var title = reader.ReadLine();
var content = reader.ReadToEnd();
return (title, content);
}
throw new Exception("文章为空!");
}
async Task<bool> PublishArticleAsync(string translatorContent)
{
var (title, content) = SplitArticle(translatorContent);
var url = "";
this.Invoke(() =>
{
url = UrlTextBox.Text;
});
if (!string.IsOrWhiteSpace(url))
{
content += $"\r\n元のリンク:{url}";
}
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = false,
Args = ["--start-maximized"]
});
var page = await browser.NewPageAsync();
await page.GotoAsync("https://qiita.com/");
await page.ClickAsync("a[href='/login?callback_action=login_or_signup&redirect_to=%2F&realm=qiita']");
var userArr = File.ReadAllLines("C:/gpt/qiita_user.txt");
await page.FillAsync("#identity", userArr[0]);
await page.FillAsync("#password", userArr[1]);
await page.ClickAsync("input[name='commit']");
await page.GotoAsync("https://qiita.com/drafts/new");
await page.FillAsync("input[placeholder='記事タイトル']", title);
await page.FillAsync("input[placeholder='タグを入力してください。スペース区切りで5つまで入力できます。']", "C# .NET");
await page.FillAsync("div[role='textbox']", content);
MessageBox.Show("请确认发表内容,并且手动在弹出的内核浏览器中发布!请注意,点击确定后会自动关闭内核浏览器!!!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return true;
}
async Task<(string Title, string Content)> GetArticleAsync(string url)
{
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(/*new BrowserTypeLaunchOptions { Headless = false }*/);
var page = await browser.NewPageAsync();
await page.GotoAsync(url);
var title = await page.Locator("#activity-name").InnerTextAsync();
var locator = page.Locator("#js_content");
var images = await locator.GetByRole(AriaRole.Img).ElementHandlesAsync();
var imageList = new List<string>();
foreach (var image in images)
{
var imgUrl = await image.GetAttributeAsync("data-src");
imageList.Add(imgUrl);
}
var html = await locator.InnerHTMLAsync();
var imgTagPattern = @"<img[^>]*>";
html = Regex.Replace(html, imgTagPattern, "[图片]");
await page.SetContentAsync("<div id='js_content'>" + html + "</div>");
var content = await page.Locator("#js_content").InnerTextAsync();
var reader = new StringReader(content);
var index = 0;
var contentBuilder = new StringBuilder();
while (true)
{
var line = await reader.ReadLineAsync();
if (!string.IsOrWhiteSpace(line.Trim()))
{
if (line.Trim() == "[图片]")
{
contentBuilder.AppendLine($"");
index++;
}
else
{
contentBuilder.AppendLine(line);
}
}
if (reader.Peek() <= 0)
{
break;
}
}
return (title, contentBuilder.ToString());
}
async Task<string> OpenAIChatSampleAsync(string title, string content)
{
var key = File.ReadAllText(@"C:\GPT\key.txt");
var chatModelId = "gpt-4-0125-preview";
OpenAIChatCompletionService chatCompletionService = new(chatModelId, key);
return await StartChatAsync(chatCompletionService, title, content);
}
async Task<string> StartChatAsync(IChatCompletionService chatGPT, string title, string content)
{
var lan = "日文";
this.Invoke(() =>
{
lan = LanComBox.Text;
});
var prompt = File.ReadAllText(Environment.CurrentDirectory + "/Prompt.md");
prompt = prompt.Replace("{{language}}", lan);
var chatHistory = new ChatHistory(prompt);
var userContent = $"标题:{title}\r\n内容:{content}";
chatHistory.AddUserMessage(userContent);
return await MessageStreamOutputAsync(chatGPT, chatHistory);
}
async Task<string> MessageStreamOutputAsync(IChatCompletionService chatGPT, ChatHistory chatHistory)
{
var list = chatGPT.GetStreamingChatMessageContentsAsync(chatHistory);
var fullMessage = string.Empty;
await foreach (var item in list)
{
if (item == )
{
continue;
}
fullMessage += item.Content;
this.Invoke(() =>
{
TranslationTextBox.AppendText(item.Content);
});
}
return fullMessage;
}
private void TranButton_Click(object sender, EventArgs e)
{
try
{
var url = UrlTextBox.Text;
if (string.IsOrWhiteSpace(url))
{
MessageBox.Show("输入的url有误,请重新输入!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
var pattern = @"^(https?:\/\/)?(?s).*$";
var regex = new Regex(pattern, RegexOptions.IgnoreCase);
bool isValid = regex.IsMatch(url);
if (!isValid)
{
MessageBox.Show("输入的url有误,请重新输入!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
else
{
Task.Run(() =>
{
try
{
var (title, content) = GetArticleAsync(url).Result;
this.Invoke(() =>
{
OriginalTextBox.Lines = new string[] { title, content };
});
var translatorContent = OpenAIChatSampleAsync(title, content).Result;
var result = PublishArticleAsync(translatorContent).Result;
if (!result)
{
MessageBox.Show("翻译失败!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception exc)
{
MessageBox.Show(exc.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
});
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void LanComBox_SelectedIndexChanged(object sender, EventArgs e)
{
this.Text = "中文->" + LanComBox.Text;
}
private void ClearButton_Click(object sender, EventArgs e)
{
UrlTextBox.Clear();
OriginalTextBox.Clear();
TranslationTextBox.Clear();
}
}
}
运行结果一:
运行结果二:
国长安网北京8月1日电(郭莎莎)公安部今天在京发布,公安部“互联网+政务服务”平合正式上线,实现全国一次认证、一网通办。
公安部科技信息化局局长厉剑通报,公安部“互联网+政务服务”平台今天正式上线,网址为https://zwfw.mps.gov.cn/。普通网友均可登录使用。平台已对接贯通国家政务服务平台,统一了用户体系,解决了企业和群众在公安平台和政府平台重复注册、多次验证等问题,实现了一次认证、单点登录、全网通办。
“平台的上线,是公安机关为民服务的又一创新成果,也标志着全国公安政务服务工作水平迈上了一个新台阶。”厉剑说。
他介绍,“互联网+公安政务服务”是公安机关在深入调研、充分听取企业、群众和民警意见的基础上,敢于刀刃向内,打破数据和系统壁垒,运用互联网思维,通过数据共享和业务流程再造,减少警种部门之间和上下级公安机关之间审批流转的中间环节,最大程度地简化群众办事流程,促进服务审批工作更加高效,让企业、群众充分得到公安“放管服”改革的红利和实惠,同时也释放出更多的警力从事治安防范、打击犯罪等其他公安业务工作。
公安部科信委专职副主任刘明芳介绍,公安部的平台综合集成了各层级公安机关和公安部各警种、各部门的网上办事系统,形成了集中的办事中心、查询中心、评价中心,主要有以下三个特色:
一是统一应用支撑。平台建设了统一身份认证、统一事项管理、统一数据交换、统一评价服务等基础支撑功能,为各地公安机关网上政务服务平台提供共性应用支撑,各地平台可在此基础上,开发符合本地实际的个性化应用。
二是四级业务联动。平台强化协同联动,将原来分布在部、省、市、县四级公安机关的网上办事平台与公安部“互联网+政务服务”平台逐级对接贯通,将四级公安机关网上政务服务的数据流、业务流、管理流有效融合,建立了网上政务服务动态更新机制,确保公安政务服务事项的源头一致,更新同步,服务同频,实现群众办理公安政务服务事项只进一张网、不用多头跑。
三是身份双源认证。以全国人口信息和居民身份证办理信息作为网上身份信息核验的两大权威数据源,支撑全国一体化在线政务服务平台用户身份信息的实人、实名、实证的在线核验,有效解决了网上政务服务“我就是我”、“是我在办”的难题。
记者从发布会获悉,各级公安机关紧贴群众需求,勇于推陈出新,不断增强“互联网+公安政务服务”工作的创新活力。通过网站、APP、公众号、小程序,将服务的入口从线下移到线上;通过在线申请、网上受理、远程寄递等多种方式,有效打通了网上服务的“最后一公里”通过刷脸认证、一键挪车等便捷应用,将服务的触角延伸至各个角落。
陕西作为全国首批4个试点对接公安部“互联网+平台”的省份,率先建成陕西“互联网+公安政务服务”平台并正式上线运行。在平台建设应用推广中,结合地方特色,重点从优化办事流程、打通关键环节、完善平台问题三方面来打通网上办事的“最后一公里”。广东省公安机关按照有关要求,打造了既支持内地居民也服务港澳居民注册和使用的“智慧新民生”平台,并且实现了和公安部“互联网+政务服务”平台的全面对接。目前,可面向港澳居民在线办理的有粤港澳直通车、警务非税收入缴款两方面业务。
公安部“互联网+政务服务”平台目前已汇聚服务事项548项,为群众提供了公安服务事项的办理、查询、评价一站式服务,打造了“一大平合全网贯通、双源数据全面支撑、四级联动全警应用、多种措施全力防护”的应用体系。
厉剑提及,信息安全与服务同样重要。通过将技术防护和安全管理的有效结合,着力构建全方位、多层次的纵深安全防护体系,确保公安部“互联网+政务服务”平台的系统安全和数据安全。
在技术上,该平台采取五方面的措施:
一是邀请信息安全方面的专家和相关专业力量,制定了“云+端”的安全技术方案。二是采用安全网关、防火墙、入侵防护、堡垒机等各种安全加固措施抵御外部的攻击,保障平台信息安全。三是建设安全风险发现预警功能,出现安全漏洞时及时预警,并能阻断数据外泄。四是利用国产加密算法、身份鉴权等相关技术,保障接口安全及数据的加密传输和存储安全。目前已经通过密码应用安全方面的测评。五是严格对照信息系统等级保护相关要求,配置相应的安全防护设备和防护手段。目前,已经通过了等保三级相关测评。
在管理上,该平台采取了三个方面的安保措施。一是制定统一的安全管理规定,明确各级公安机关负责的网上办理事项网站相关的主体责任,建立了上下联动相关机制,确保公安机关一体化政务平台整体安全。二是定期对平台进行攻防演练,发现漏洞及时修补。三是建立安全事故应急预案,一旦发生安全事故后,确保能够快速响应,并及时地修复。
发布会公布,下一步,公安部将进一步整合全国公安机关的相关资源,优化流程、完善机制,不断丰富公安部“互联网+政务服务”平合功能,结合公安行业特点,聚焦“一网通办,便民惠警”目标,加强应用创新,推进线上线下深度融合,推动公安政务服务整体联动全程在线,不断扩大公安“放管服”改革的受益群体,确保让广大人民群众有更多、更直接、更实在的获得感。
EventBridge 作为构建 EDA 架构的基础设施,通过一些核心概念和特性提供了灵活丰富的事件收集、处理和路由的能力。对于不少用户来说,通过控制台里的便捷的引导来使用 EventBridge 应该是最快的上手方式。此外,也有很多用户面临着大量的云产品的管理,使用控制台管理每一个资源的方式变成了沉重的手工操作负担。
为了解决这个问题,现在已经能够通过 OpenAPI、terraform 等方式将 EventBridge 的能力方便快捷的带给用户。本文将重点介绍 EventBridge 和 IaC 的重点概念和特性,然后演示如何应用 IaC 理念自动化部署 EventBridge 来使用这些概念和特性。
事件驱动架构
事件驱动架构是一种松耦合、分布式的驱动架构,收集到某应用产生的事件后实时对事件采取必要的处理,紧接着路由至下游系统,无需等待系统响应。使用事件总线 EventBridge 可以构建各种简单或复杂的事件驱动架构,以标准化的 CloudEvents 1.0 协议连接云产品和应用、应用和应用等。
事件驱动架构体系架构具备以下三个能力:
事件驱动架构具有以下优势:
使用 EventBridge 构建 EDA 架构
事件总线 EventBridge 是阿里云提供的一款无服务器事件总线服务。EventBridge 提供的几个核心概念,可以满足构建 EDA 架构的需要。
事件总线 EventBridge 支持以下事件源:
事件总线 EventBridge 的事件总线包括以下类型:
在 EventBridge 中,一个事件规则包含以下内容:
EventBridge 提供了简洁的事件模式匹配语法,同时具备灵活的事件转换能力,后面将会通过演示来展示一些具体的例子。
此外,EventBridge 还提供了一些增强能力,这些能力使得 EDA 架构中流经的事件更加透明,具备了开箱即用的观测和分析能力:
在介绍完事件总线 EventBridge 的相关基础内容后,接下来一起了解下 IaC。在 DevOps 的实践中,IaC 是非常重要的部分,通过将基础设施代码化,版本化,便可以轻松的借助版本控制工具来提供 single source of truth、协调多人合作的变更、实施严格的 review、借助一些 CI/CD pipeline 工具(甚至 GitOps)来自动触发部署。软件系统的开发者仅付出很小的努力去描述需求,就可以在几分钟后得到所需的虚拟机、网络等云上的服务,极大的缩短了部署时间,同时还能够保证多个环境的配置一致性,通过减少人为操作也降低了引入错误的概率。
IaC的代码实践中一般有两种方式,命令式和声明式。
IaC 带来的优势:
terraform 作为 IaC 领域的佼佼者,提供了强大的自动化管理基础设施的能力。生态丰富,很多云厂商都提供了官方插件,阿里云的大多数产品(包括 EventBridge)都对 terraform 做了很全面的支持,使得跨多云部署基础设施变得极其简单。既然是 IaC,terraform 提供了自己的语言 HCL(hashicorp configuration language),HCL 具有类似 json 的简洁的语法,通过声明式的资源描述,可以让开发者快速上手。
准备工作
provider "alicloud" {
access_key = "<your access key>"
secret_key = "<your secret key>"
region = "<region id>"
}
案例1:通过钉钉监控云上资源变化
假设一个用户使用了很多云上的资源作为生产环境,需要感知线上资源的变更操作,一个可行的方案是利用 EventBridge 将来自于 ActionTrail 的审计事件投递到用户的钉钉。
首先根据钉钉官方文档创建一个机器人,记下 webhook url 和加签的秘钥,接下来会用到。
创建一个 tf 文件 1_actiontrail2dingding.tf,内容如下(需要替换<>内的值)
# 案例1:通过钉钉监控云上资源变化
# 目标:
# - 熟悉部署使用EventBridge的default总线
# - 熟悉EventBridge的事件模式匹配
# - 熟悉EventBridge的事件转换配置
# 声明一个default总线上的规则
resource "alicloud_event_bridge_rule" "audit_notify" {
# default总线默认存在,所以这里可以直接使用
event_bus_name = "default"
rule_name = "audit_notify"
description = "demo"
# 通过后缀匹配的方式过滤来自所有云产品事件源的ActionTrail:ApiCall事件
# 其他更多模式匹配的介绍可以查阅文档:https://help.aliyun.com/document_detail/181432.html
filter_pattern = jsonencode(
{
"type" : [
{
"suffix" : ":ActionTrail:ApiCall"
}
]
}
)
targets {
target_id = "test-target"
endpoint = "<your dingtalk bot webhook url>"
# type的取值可以查阅文档:https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/event_bridge_rule#type
type = "acs.dingtalk"
# 每个事件目标都有一组对应的param_list,具体可以查阅文档:https://help.aliyun.com/document_detail/185887.html
# 每一个param的form关系到事件转换的配置,可以查阅文档:https://help.aliyun.com/document_detail/181429.html
param_list {
resource_key = "URL"
form = "CONSTANT"
value = "<your dingtalk bot webhook url>"
}
param_list {
resource_key = "SecretKey"
form = "CONSTANT"
value = "<your dingtalk bot secret key>"
}
# 这里展示了TEMPLATE类型的事件转换描述
# value是使用jsonpath引用事件内容的字典,template则是模板内容,EventBridge最终会根据这两者结合事件本身渲染出这个参数的值
param_list {
resource_key = "Body"
form = "TEMPLATE"
value = jsonencode(
{
"source": "$.source",
"type": "$.type"
"region": "$.data.acsRegion",
"accountId" : "$.data.userIdentity.accountId",
"eventName" : "$.data.eventName",
}
)
template = jsonencode(
{
"msgtype" : "text",
"text" : {
"content": "来自 $${source} 的 $${type} 审计事件:$${accountId} 在 $${region} 执行了 $${eventName} 操作"
}
}
)
}
}
}
在命令行窗口依次执行命令:
在云产品控制台进行操作,这里以 KMS 为例
钉钉上收到消息通知
在 EventBridge 控制台查看事件轨迹
案例 2:自定义总线触发 FunctionCompute
假设一个用户的应用会产生一些事件,其中一个链路是通过 FunctionCompute 对这些事件进行弹性的处理。那么就可以通过 EventBridge 的自定义事件源和函数计算事件目标来实现这个方案。
创建一个模拟对事件进行处理的 python 脚本文件 src/index.py,内容如下:
# -*- coding: utf-8 -*-
import logging
def handler(event, context):
logger = logging.getLogger()
logger.info('evt: ' + str(event))
return str(event)
创建一个 tf 文件 2_trigger_function.tf,内容如下(需要替换<>内的值)
# 案例2:自定义总线触发FunctionCompute
# 目标:
# - 熟悉部署使用EventBridge的自定义总线
# - 熟悉"自定义应用"事件源配置
# - 熟悉“FunctionCompute”事件目标配置
# 由于用户自己产生的事件需要投递到自定义总线,这里声明一个叫demo_event_bus的自定义总线
resource "alicloud_event_bridge_event_bus" "demo_event_bus" {
event_bus_name = "demo_event_bus"
description = "demo"
}
# 声明一个在demo_event_bus总线上的自定义事件源,用于通过sdk或者控制台向EventBridge投递事件
resource "alicloud_event_bridge_event_source" "demo_event_source" {
event_bus_name = alicloud_event_bridge_event_bus.demo_event_bus.event_bus_name
event_source_name = "demo_event_source"
description = "demo"
linked_external_source = false
}
# 声明一个叫fc_service的函数计算服务,publish=true意味着会立即部署上传的函数代码。
resource "alicloud_fc_service" "fc_service" {
name = "eb-fc-service"
description = "demo"
publish = true
}
# 将前面准备的python脚本文件打包成zip用于部署到函数计算
data "archive_file" "code" {
type = "zip"
source_file = "${path.module}/src/index.py"
output_path = "${path.module}/code.zip"
}
# 声明一个fc_service服务中的函数,其中filename引用了上面描述的zip包,会将这个代码包上传。
resource "alicloud_fc_function" "fc_function" {
service = alicloud_fc_service.fc_service.name
name = "eb-fc-function"
description = "demo"
filename = data.archive_file.code.output_path
memory_size = "128"
runtime = "python3"
handler = "index.handler"
}
# 声明一个在demo_event_bus总线上的规则
resource "alicloud_event_bridge_rule" "demo_rule" {
event_bus_name = alicloud_event_bridge_event_bus.demo_event_bus.event_bus_name
rule_name = "demo_rule"
description = "demo"
# 通过匹配source过滤来自于前面创建的自定义事件源的事件
filter_pattern = jsonencode(
{
"source" : ["${alicloud_event_bridge_event_source.demo_event_source.id}"]
}
)
targets {
target_id = "demo-fc-target"
# type的取值可以查阅文档:https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/event_bridge_rule#type
type = "acs.fc.function"
endpoint = "acs:fc:<region id>:<your account id>:services/${alicloud_fc_service.fc_service.name}.LATEST/functions/${alicloud_fc_function.fc_function.name}"
param_list {
resource_key = "serviceName"
form = "CONSTANT"
value = alicloud_fc_service.fc_service.name
}
param_list {
resource_key = "functionName"
form = "CONSTANT"
value = alicloud_fc_function.fc_function.name
}
param_list {
resource_key = "Qualifier"
form = "CONSTANT"
value = "LATEST"
}
# 注意form=ORIGINAL意味着每次投递事件都会将事件的原始内容作为这个参数的值
param_list {
resource_key = "Body"
form = "ORIGINAL"
}
}
}
在命令行窗口依次执行命令
在控制台模拟自定义事件源发布事件
在 FunctionCompute 的控制台页面查看函数调用日志
在 EventBridge 控制台查看事件轨迹
总结
EventBridge 作为构建 EDA 架构的基础设施,通过一些核心概念和特性提供了灵活丰富的事件收集、处理和路由的能力,并支持通过 OpenAPI、terraform 等方式将这些能力方便快捷的带给用户。本文介绍了 EventBridge 和 IaC 的重点概念和特性,然后演示了如何应用 IaC 理念自动化部署 EventBridge 来使用这些概念和特性。
期待大家可以发掘更多利用 EventBridge 快速搭建 EDA 架构的 idea,并使用 terraform 快捷的将这些 idea 变为现实。
[1] 阿里云 terraform 文档
https://help.aliyun.com/product/95817.html
[2] terraform registry 文档
https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/event_bridge_event_bus
[3] 钉钉官方文档
https://open.dingtalk.com/document/group/custom-robot-access
作者:王川(弗丁)
原文链接:https://developer.aliyun.com/article/882907?utm_content=g_1000334839
本文为阿里云原创内容,未经允许不得转载。
*请认真填写需求信息,我们会在24小时内与您取得联系。